One command, idempotent
Read the MX constraint first — it is the one decision here you cannot take back casually. pithy add support wrote the wiring; pithy support is the command; Using Support is what you do afterwards.
pithy support provision \
--routing-zone <zone-id> \
--inbound-address support@help.example.com \
--app-worker apiSafe to re-run. --json prints one machine-readable line, so CI and agents drive the same command a human does.
Three things have to exist, and none can be a hand-written binding
| An R2 bucket | Attachments and raw messages |
| A classification Worker | Per environment, prebuilt |
| An Email Routing rule | The one that actually delivers the mail |
The bucket is created only if something would write to it
Mail attachments, the raw MIME copy, or in-app uploads.
Turn all three off and no bucket is created and no binding is written — which is the right shape for an inbox that never needs a screenshot.
Turn any one back on and the next pithy upgrade writes the binding, and the next provision creates the bucket.
The routing rule is opt-in, and that is a safety decision
So the zone, the address and the target Worker are all explicit flags, all-or-nothing.
A project that has not decided yet provisions everything else and adds the rule later.
Read the MX constraint before you pass those flags. It is the single most important thing to know about this capability.
The rule name carries the project
<project>-global-support-inbound — deliberately distinct from the email capability’s bounce rule.
Idempotency keys on the rule name. Sharing one would make whichever capability provisioned second silently believe its rule already existed.
The project segment stops two Pithy projects on one zone doing that to each other, with the loser’s customer mail delivered to the winner’s Worker.
Teardown removes the rule first
pithy support deprovision --routing-zone <zone-id>Mail stops arriving before the Workers that would have handled it go away.
Your stored correspondence is left alone unless you pass --storage.
The zone is named for the same reason it is named on provision: a rule is addressed through its zone, and this command will not go looking through your domains for one. Leave it off and the rule stays — which the command tells you.
Everything is idempotent
Find-then-create for resources, a deploy that overwrites, and a routing step that skips cleanly when no routing config was supplied.
The live Cloudflare and wrangler steps sit behind a seam, so the ordering, the idempotency contract and the per-environment fan-out are unit-tested without touching Cloudflare.