Email API

/

HTTP

Self-Hosted Email API with HTTP

This is a complete self-hosted email API example over plain HTTP. Copy the curl examples below, point them at your Cloudflare Worker, and send or receive email in under five minutes — no SDK, no SMTP relay, no per-email bill. Every request is bearer-authenticated against a Worker running in your own Cloudflare account, and the inbox and outbox land in your D1 database.

Set up the D1 schema

The inbox lives in your Cloudflare D1 database. Apply this schema once with wrangler d1 execute before you send the first message, and the Worker will start writing every inbound envelope into the same rows your dashboard reads.

schema.sqlsql
CREATE TABLE IF NOT EXISTS messages (
  id TEXT PRIMARY KEY,
  mailbox TEXT NOT NULL,
  from_address TEXT NOT NULL,
  to_address TEXT NOT NULL,
  subject TEXT,
  body_text TEXT,
  body_html TEXT,
  received_at INTEGER NOT NULL,
  thread_id TEXT
);
CREATE INDEX IF NOT EXISTS idx_messages_mailbox
  ON messages(mailbox, received_at DESC);

Send your first message

POST /api/email/send on your deployed Worker with a JSON body. The from address must land on a domain you have bound to the API key — the Worker rejects cross-domain sends with a 422 from_domain_mismatch.

send.shbash
curl -X POST https://your-worker.workers.dev/api/email/send \
  -H "Authorization: Bearer fm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "hello@yourdomain.com",
    "to": "customer@example.com",
    "subject": "Order #1234 confirmed",
    "text": "Thanks for your order — we will ship it tomorrow."
  }'

# 201 Created
{
  "results": [
    { "id": "msg_8421", "status": "queued" }
  ]
}

Receive inbound webhooks

Cloudflare Email Routing hands every inbound message to your Worker. Persist the envelope to your D1 inbox, then forward the event to any webhook your product cares about — a CRM, a ticket queue, or a Slack channel.

worker.tsts
# apps/web/your-worker/src/index.ts
export default {
  async email(message, env, ctx) {
    // Cloudflare Email Routing hands every inbound message to your Worker.
    // Persist the envelope to your D1 inbox, then forward the event to
    // any webhook your product cares about.
    const payload = {
      messageId: message.headers.get("message-id"),
      from: message.from,
      to: message.to,
      subject: message.headers.get("subject"),
    };
    await fetch("https://your-app.example.com/webhooks/inbound", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
  },
};

Send your first email from a Cloudflare Worker in under five minutes.

Start free