POST /v1/orders call — this page doesn’t add behavior, it gives the
existing spend-safety layer one name and one home: Send Policies.
Send Policies is the set of rules paperplane enforces so that an autonomous
agent can be trusted with something a support script never could — spending
real money on an irreversible, physical action. Four mechanisms make up the
policy, and all four apply together on every send:
Because paperplane has no accounts and no API keys, none of these can fall
back on “the caller is authenticated.” Every guarantee has to come from the
token itself — that’s why Send Policies is built from signed, narrowly-scoped
tokens rather than a session or a role.
Confirmation tokens
A send is never the first call.POST /v1/quotes (or the quote_letter MCP
tool) is free, creates nothing, and returns a confirmation_token (ppq_)
cryptographically bound to the exact recipient, content, mail class, and
price it just quoted.
POST /v1/orders on the agent path requires that token, and the server
re-derives the binding from the order body — if anything changed since the
quote, the token doesn’t match and the order is refused. This guarantees an
agent can never send a letter for content it never priced, and a human can
never be charged for something other than what they approved.
Confirmation tokens are:
- Single-use. Redeeming one sends the letter; presenting it again returns
confirmation_used, not a second letter. - Short-lived. 30-minute TTL —
confirmation_expiredpast that window, and quoting again is the only recovery. - Exact-match. Any change to recipient, content, mail class, or price
invalidates it (
confirmation_invalid).
Idempotency keys
Confirmation tokens stop an agent from sending content it never priced; idempotency keys stop a retry from sending it twice. Pass anIdempotency-Key header on every POST /v1/orders call — a retried request
with the same key returns the original order ("replayed": true, same id)
instead of creating a new one.
This is what makes retrying after a timeout safe by default: a 429, 503,
or dropped connection is never a reason to worry about a duplicate charge or
a duplicate letter, as long as the retry reuses the same key. See
Retry semantics for how this combines with
each error code.
Idempotency keys are scoped per caller, never global. The same key string
used by two different callers (your API key id, else your signed-in account,
else the client address the edge observed) claims two independent orders —
one caller can never be handed back another caller’s order, or that order’s
cancel_token/review_token, by reusing a common key like "1" or today’s
date. If two requests are genuinely retries of the same send, reuse the same
key from the same caller; a new key, or a key sent by a different caller,
always mails (or attempts) a new letter.
An in-flight duplicate — the first request with a key hasn’t finished yet —
returns idempotency_key_in_flight (409, Retry-After: 5) rather than
racing a second create; retry with the same key to receive that first
request’s order once it lands.
Capability tokens
Because there’s no account to authenticate, doing something to an existing order — after it’s been created — needs its own signed grant. Capability tokens (ppc_) are minted at order creation and returned under
capability:
Both are bound to the specific order id and the specific action — a
cancel_token can’t be replayed against /v1/reviews, and vice versa — and
both expire after 7 days. Possession of an order id alone is never
enough to cancel or review it; see Cancel & track for
the full cancellation flow, including why the state has to be cancelable in
the first place.
Capability errors are 403s: invalid_capability when the token doesn’t
match this order + action, expired_capability past the 7-day window.
Sandbox vs live
The fourth piece of the policy is which world a send happens in. Sandbox mode runs the complete pipeline — rendering, address verification, screening, simulated fulfillment with a mock tracking number — instantly and free, withsandbox: true and zero keys. Live mode is the
same call without that flag, and requires either a human-approved Stripe
payment_url or a prepaid credit_code before anything prints.
Confirmation tokens, idempotency keys, and capability tokens all apply
identically in both modes — sandbox is not a relaxed policy, it’s the same
policy with payment and physical mailing swapped for simulation. That’s what
lets an agent (or a person evaluating the API) exercise the full send policy
before any card or real mail is involved.