Questions · Updated 2026-09-23

How do I send email from a Cloudflare Worker?

Send email from a Cloudflare Worker with the Hono route below. Pass the key from the Worker binding into the client, then call POST /emails. The same file runs on Workers because the client is fetch.

Send email from a Cloudflare Worker with the Hono route below. The Hono guide says the client is plain fetch with no Node built-ins, so the same file runs on Cloudflare Workers, and that on Workers you pass the key from the binding: new AgentiSend(c.env.AGENTISEND_API_KEY). The call that route makes is POST /emails. Read the result with GET /emails/{id}. Create the key with POST /api-keys.

The route

This file is executed against a real API on every build of this site.

/**
 * Hono — one POST route that sends a transactional email.
 *
 * Hono runs on Workers, Deno, Bun and Node, and the AgentiSend SDK is plain
 * fetch with no Node built-ins, so this file is the same on all of them.
 */
import { Hono } from 'hono';
import { AgentiSend, AgentiSendError } from 'agentisend';

const agentisend = new AgentiSend();

function mailFrom(): string {
  const from = process.env.MAIL_FROM;
  if (!from) throw new Error('Set MAIL_FROM to an address on a domain you have verified.');
  return from;
}

export const app = new Hono();

app.post('/send', async (c) => {
  const { email, subject, body } = await c.req.json<{
    email?: string;
    subject?: string;
    body?: string;
  }>();
  if (!email) return c.json({ error: 'email is required' }, 400);

  try {
    const { id } = await agentisend.emails.send(
      {
        from: mailFrom(),
        to: email,
        subject: subject ?? 'Your receipt',
        text: body ?? 'Thanks — your receipt is attached to your account.',
      },
      { idempotencyKey: `receipt/${email}` },
    );
    return c.json({ id });
  } catch (err) {
    if (err instanceof AgentiSendError) {
      // `fix` names the endpoint that repairs the call, so a client can act on
      // the failure without a human reading prose.
      return c.json({ code: err.code, fix: err.fix }, 502);
    }
    throw err;
  }
});

export default app;
examples/hono/app.ts, executed against the live API on every build

The file reads AGENTISEND_API_KEY from the process environment when the constructor is called with no argument. On a Worker, use the binding the Hono guide names.

The key

As of September 2026, Cloudflare's secrets documentation says not to put a secret in vars, and that a secret is available on env the same way an environment variable is. The page is Secrets. The environment-variables page says a variable is on the env argument of the fetch handler: Environment variables.

MAIL_FROM in the example must be an address on a domain POST /domains/{id}/verify has verified. A send before that returns domain_not_verified. To rehearse with no delivery, address the recipient at simulator.agentisend.com, as the test page describes.

Next