---
title: "Primitive Functions and Recipient Routing"
canonical: "https://test.abhinandan.one/functions-and-routing-concepts"
markdown_url: "https://test.abhinandan.one/functions-and-routing-concepts.md"
publisher: "Primitive SDKs"
kind: "concept"
content_type: "reference"
category: "Product Surfaces"
description: "Primitive Functions run JavaScript handlers on inbound mail, and recipient routing rules bind an address pattern to the function or endpoint that handles it."
keywords: ["Primitive Functions", "recipient routing", "primitive functions deploy", "primitive routes add", "RoutingDecision", "route-target endpoint"]
last_modified: "2026-08-21T18:22:43.359885+00:00"
published_at: "2026-08-11T18:54:58.066005+00:00"
source_files:
  - "cli-node/src/oclif/commands/functions-deploy.ts"
sections:
  - {anchor: "what-a-primitive-function-is", title: "What a Primitive Function is"}
  - {anchor: "what-recipient-routing-decides", title: "What recipient routing decides"}
  - {anchor: "deploying-a-function-from-the-cli", title: "Deploying a Function from the CLI"}
  - {anchor: "deployed-doesnt-mean-reachable", title: "Deployed doesn't mean reachable"}
  - {anchor: "binding-recipient-routing-rules", title: "Binding recipient routing rules"}
  - {anchor: "next-steps", title: "Next steps"}
---

> Documentation index: https://test.abhinandan.one/llms.txt

# 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](https://test.abhinandan.one/email-model.md) and [Webhook Events Overview](https://test.abhinandan.one/webhook-events.md).

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

```mermaid
flowchart LR
    A[Inbound email] --> B{Recipient routing rule matches?}
    B -- "exact or wildcard match" --> C[Bound destination]
    C --> D[Function route-target endpoint]
    C --> E[Existing webhook endpoint]
    B -- "no match" --> F[Default / catch-all endpoint]
```

## 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](https://test.abhinandan.one/cli-overview/cli-functions.md) 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:

```text
Route bound. Function will receive inbound mail.
```

or

```text
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:

```bash
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.

> **Note:** 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.
