Primitive Functions and Recipient Routing
Primitive Functions are serverless email handlers deployable from the CLI, and recipient routing decides which endpoint or function processes each inbound address, the two platform primitives every SDK and the CLI build on.
What a Primitive Function is#
A Primitive Function is a single ESM module, deployed to Primitive's edge runtime, whose default export is an object with an async fetch(request, env) method, the same shape as a Cloudflare Workers-style handler. Primitive invokes it on inbound mail: it signs the delivery, forwards the Primitive-Signature header to the handler, and expects the handler to verify the raw request body against PRIMITIVE_WEBHOOK_SECRET before trusting the parsed payload. There is no infrastructure to provision; secrets you configure land in env as encrypted bindings, refreshed on every redeploy.
The payload's event field is email.received for ordinary inbound mail, or a machine-mail type (email.bounced, email.tls_report, email.dmarc_report, email.dmarc_failure) for bounces and reports; the payload shape is otherwise identical. For the normalized email object and the full webhook event catalog, see Inbound and Outbound Email Model and Webhook Events Overview.
What recipient routing decides#
Recipient routing is a rule set that binds a recipient address (exact match or wildcard) to exactly one destination: either an existing webhook endpoint, or a Primitive Function. The webhook payload carries a RoutingDecision field describing how the inbound email was routed.
Binding an address to a function mints that function's route-target endpoint in the same call, so you don't create the endpoint yourself first. Recipient routing is gated by an organization entitlement; rules are inert until that entitlement is enabled for your org.
Deploying a Function from the CLI#
The CLI's primitive functions command group scaffolds, builds, and ships Functions; see Primitive Functions: Deploy, Route, and Manage for the full deploy workflow, secret-source flags, and --wait behavior.
Deployed doesn't mean reachable#
A successful deploy means the Function's code is live, not that inbound mail reaches it. Mail only flows to a Function once a route points at it. After every deploy the CLI checks routing status and prints one of:
Route bound. Function will receive inbound mail.
or
Deployed but no route is bound. Inbound mail will not reach this function until
you bind one: primitive functions route-set --id <function-id> --domain <domain-id> (or --fallback)
That is the common first-deploy trap: testing an inbound flow against a Function with no route bound yet, and getting silence instead of an error.
Binding recipient routing rules#
Bind an address to a destination with the CLI's primitive routes command group, which also lists, tests, reprioritizes, and removes rules:
primitive routes add alice@example.com --function <function-id>
primitive routes add 'support+*@example.com' --match wildcard --endpoint <endpoint-id>
primitive routes list
primitive routes test alice@example.com
primitive routes update <route-id> --priority 5
primitive routes reorder --set <route-id>=10 --set <other-id>=20
primitive routes remove <route-id>
| Flag | Destination | Effect |
|---|---|---|
--function <function-id> | A Primitive Function | Mints the function's route-target endpoint and binds the address to it in one call |
--endpoint <endpoint-id> | An existing webhook endpoint | Binds the address to that endpoint directly |
--match wildcard accepts a pattern like support+*@example.com for plus-addressing or catch-all-style matches; the default match mode is exact. primitive routes test <address> previews where an address resolves and prints the rule trace, so you can confirm a routing change without waiting for real inbound mail. primitive routes reorder sets explicit priorities across multiple rules in one call when several patterns could match the same address.
Recipient routing rules do nothing until your org's routing entitlement is enabled. If routes add succeeds but mail still lands on the default endpoint, confirm the entitlement is on before debugging the rule itself.
Next steps#
Full flag reference for functions-* commands: templates, redeploy, test runs, secrets, and logs.
Managing Domains and Recipient RoutesThe full primitive routes command group plus inbound domain DNS configuration.
Webhook Events OverviewThe shared webhook contract every Function payload follows: signature verification and event catalog.
Inbox Setup and StatusCheck whether an inbox is actively processed or stored-only, and diagnose missing routes.
Was this page helpful?