Skip to main content
Every mechanism on this page already exists and is enforced by the server on every 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_expired past that window, and quoting again is the only recovery.
  • Exact-match. Any change to recipient, content, mail class, or price invalidates it (confirmation_invalid).
See the full set of confirmation-related codes in the error contract.

Idempotency keys

Confirmation tokens stop an agent from sending content it never priced; idempotency keys stop a retry from sending it twice. Pass an Idempotency-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, with sandbox: 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.

Send Policies at a glance

Every layer fails closed: no token means no action, an expired or mismatched token means no action, and a replayed idempotency key means no second order. An agent following this contract can be handed a payment method and mail real letters without a human in the loop for anything except the payment approval itself — see Build with agents for why that’s a reasonable thing to automate.
Last modified on September 15, 2026