What the Cloudflare Email Routing REST API actually does
The Cloudflare API exposes Email Routing as a small set of endpoints under /zones/{zone_id}/email/routing. You can list, create, update, and delete routing rules; you can list, create, and delete catch-all rules; you can list and verify destination addresses; you can read the address verification status; you can enable or disable the feature on the zone. None of those endpoints takes a message body and none of them tells the platform to deliver mail. They are configuration endpoints, not delivery endpoints.
The corresponding @cloudflare/workers-types binding for an inbound Worker — the email handler — is also inbound-only. It receives a ForwardableEmailMessage with a from, a to, a raw RFC 5322 stream, and a forward() method that pushes the message to a verified destination. There is no symmetric send() on the binding. That is not an oversight — it is the product boundary.
Why Email Routing does not send
The product boundary is intentional. Email Routing exists to answer one question: "mail was sent to anything@yourdomain.com; what do we do with it?" The answer is either forward it to a real mailbox or hand it to a Worker. Cloudflare does not need to hold an inbox for you, does not need to maintain a sender reputation for outbound, and does not need to manage DKIM signing on outgoing mail. The platform is the MX, not the MUA.
Sending is a fundamentally different problem: it requires DKIM keys that rotate, an SPF policy on the outbound envelope, IP reputation that has to be earned, bounce handling, FBL feedback loops, suppression lists, and the queueing logic that retries with backoff when Gmail temporarily rejects a message. That is a separate primitive — Cloudflare Email Services — and it lives on its own send-side binding exposed to Workers. The two products share the zone and the domain but they do not share an endpoint or a contract.
The Cloudflare-native send path
To send from hello@yourdomain.com on Cloudflare you need three pieces. The first is Email Services enabled on the zone (one call to the Email Services API). The second is a Worker with the Email Services send binding — that is the send_email exported function that takes a structured payload and returns a message id. The third is a REST surface in front of that Worker so your service code does not need to embed a Cloudflare API token in every backend.
The Worker handler looks roughly like this: receive a JSON POST with { from, to, subject, text }, validate the auth header (Cloudflare service auth or a shared bearer signed against your secret), call env.SEB.send_email({ from, to, subject, text }), and return the message id. The whole handler is about 30 lines of TypeScript. The runtime tour post walks through the same envelope from the inbound side.
A working REST API example
The Worker routes that front the send primitive expose a stable REST shape. The contract is POST /api/email/send with an application/json body of { from, fromName?, to, subject?, text?, content?, attachments?, threading? }. The response is { results: [{ messageId, accepted, error? }] }. The same endpoint is used for both new sends and replies; the only discriminator is the presence of a threading block, which carries the SMTP In-Reply-To and References headers.
From your service code the call is a normal fetch: fetch("https://your-worker.workers.dev/api/email/send", { method: "POST", headers: { "content-type": "application/json", authorization: `Bearer ${WORKER_TOKEN}` }, body: JSON.stringify(payload) }). The payload shape is typed end to end in packages/shared/src/emailworker/email.ts and re-exported from the public SDK so third-party code does not have to re-declare the contract.
Where Flowmails comes in
Flowmails is the production version of that REST surface. The platform provisions the Worker in your Cloudflare account, points it at your D1, signs the send binding, and exposes the same POST /api/email/send contract — plus the read endpoints, the route tables, the inbox UI, the AI draft, the webhook fan-out — through a single dashboard and a single SDK. The Worker itself is still your code, in your account, on your DNS. Flowmails is the management plane on top.
The public SDK lives at sdk.flowmails.net and the NPM package is @flowmails/sdk. The API surface and the auth model are documented under /docs. If you want the full platform — inbox UI, send/receive API, route tables, AI draft — connect a Cloudflare account and Flowmails sets the whole thing up in about a minute.