Files
dotfiles/dot_agents/skills/cloudflare/references/email-routing/api.md
T
Prad Nukala 6406208b35 shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables:
  ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone
  ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj
  (ghg-clone not named ghg: unrelated cargo binary owns that name)
- cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho
  cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files
- fish keeps only shell-state functions; converted ones now hit PATH
- cp-appid ignore gating moved to .local/bin path
- dot_agents/skills: 82 skills materialized from dotstate storage so
  dot_claude skill symlinks resolve on fresh bootstrap; dotstate
  profile/symlinks.json released .agents/skills .bashrc .zshrc
2026-08-10 15:15:06 -04:00

5.2 KiB

Email Routing API Reference

Worker Runtime API

Email Handler Interface

interface ExportedHandler<Env = unknown> {
  email?(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext): void | Promise<void>;
}

ForwardableEmailMessage

Main interface for incoming emails:

interface ForwardableEmailMessage {
  readonly from: string;          // Envelope sender (e.g., "sender@example.com")
  readonly to: string;             // Envelope recipient (e.g., "you@yourdomain.com")
  readonly headers: Headers;       // Web API Headers object
  readonly raw: ReadableStream;    // Raw MIME message stream
  
  setReject(reason: string): void;
  forward(rcptTo: string, headers?: Headers): Promise<void>;
}

Key Properties:

Property Type Description
from string Envelope sender (MAIL FROM), not header From
to string Envelope recipient (RCPT TO), not header To
headers Headers Email headers (Subject, From, To, etc.)
raw ReadableStream Raw MIME message (consume once only)

Methods:

  • setReject(reason): Reject email with bounce message
  • forward(rcptTo, headers?): Forward to verified destination, optionally add headers

Headers Object

Standard Web API Headers interface:

// Access headers
const subject = message.headers.get("subject");
const from = message.headers.get("from");
const messageId = message.headers.get("message-id");

// Check spam score
const spamScore = parseFloat(message.headers.get("x-cf-spamh-score") || "0");
if (spamScore > 5) {
  message.setReject("Spam detected");
}

Common Headers

subject, from, to, x-cf-spamh-score (spam score), message-id (deduplication), dkim-signature (auth)

Envelope vs Header Addresses

Critical distinction:

// Envelope addresses (routing, auth checks)
message.from // "bounce@sender.com" (actual sender)
message.to   // "you@yourdomain.com" (your address)

// Header addresses (display, user-facing)
message.headers.get("from") // "Alice <alice@sender.com>"
message.headers.get("to")   // "Bob <you@yourdomain.com>"

Use envelope addresses for:

  • Authentication/SPF checks
  • Routing decisions
  • Bounce handling

Use header addresses for:

  • Display to users
  • Reply-To logic
  • User-facing filtering

SendEmail Binding

Outbound email API for transactional messages.

Configuration

// wrangler.jsonc
{
  "send_email": [
    { "name": "EMAIL" }
  ]
}

TypeScript Types

interface Env {
  EMAIL: SendEmail;
}

interface SendEmail {
  send(message: EmailMessage): Promise<void>;
}

interface EmailMessage {
  from: string | { name?: string; email: string };
  to: string | { name?: string; email: string } | Array<string | { name?: string; email: string }>;
  subject: string;
  text?: string;
  html?: string;
  headers?: Headers;
  reply_to?: string | { name?: string; email: string };
}

Send Email Example

interface Env {
  EMAIL: SendEmail;
}

export default {
  async fetch(request, env, ctx): Promise<Response> {
    await env.EMAIL.send({
      from: { name: "Acme Corp", email: "noreply@yourdomain.com" },
      to: [
        { name: "Alice", email: "alice@example.com" },
        "bob@example.com"
      ],
      subject: "Your order #12345 has shipped",
      text: "Track your package at: https://track.example.com/12345",
      html: "<p>Track your package at: <a href='https://track.example.com/12345'>View tracking</a></p>",
      reply_to: { name: "Support", email: "support@yourdomain.com" }
    });
    
    return new Response("Email sent");
  }
} satisfies ExportedHandler<Env>;

SendEmail Constraints

  • From address: Must be on verified domain (your domain with Email Routing enabled)
  • Volume limits: Transactional only, no bulk/marketing email
  • Rate limits: 100 emails/minute on Free plan, higher on Paid
  • No attachments: Use links to hosted files instead
  • No DKIM control: Cloudflare signs automatically

REST API Operations

Base URL: https://api.cloudflare.com/client/v4

Authentication

curl -H "Authorization: Bearer $API_TOKEN" https://api.cloudflare.com/client/v4/...

Key Endpoints

Operation Method Endpoint
Enable routing POST /zones/{zone_id}/email/routing/enable
Disable routing POST /zones/{zone_id}/email/routing/disable
List rules GET /zones/{zone_id}/email/routing/rules
Create rule POST /zones/{zone_id}/email/routing/rules
Verify destination POST /zones/{zone_id}/email/routing/addresses
List destinations GET /zones/{zone_id}/email/routing/addresses

Create Routing Rule Example

curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/email/routing/rules" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "name": "Forward sales",
    "matchers": [{"type": "literal", "field": "to", "value": "sales@yourdomain.com"}],
    "actions": [{"type": "forward", "value": ["alice@company.com"]}],
    "priority": 0
  }'

Matcher types: literal (exact match), all (catch-all).