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;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.