Skip to main content

← Developers

Every error, visually

69 documented codes, each rendered exactly as the refusal card an agent sees on a real call — not a table to cross-reference by hand. The full retry-semantics writeup, with the response envelope and the idempotency guarantee, is the error contract. This page and that one are generated from the same list (lib/error-codes.ts); a build fails if they ever disagree.

Request errors — fix the input

validation_error

A field failed schema validation; `reason` lists `field: problem` pairs.

HTTP 400

invalid_request

Body was well-formed JSON but not usable as sent.

HTTP 400

invalid_page_count

page_count is not a whole number of at least 1.

HTTP 400

page_limit_exceeded

The document is longer than the 12-page maximum.

HTTP 400

tracking_included

Tracking was added to a class that already includes it — first_class only.

HTTP 400

empty_document

The PDF or text contains nothing printable.

HTTP 400

unsupported_characters

The letter text contains characters the chosen font cannot print (e.g. CJK, Arabic, or emoji). Upload as a PDF instead to send it as-is.

HTTP 400

invalid_pdf

The bytes aren't a readable PDF.

HTTP 400

invalid_base64

pdf_base64 is not valid base64.

HTTP 400

low_text_density

Scanned-image PDF. Text documents only at launch.

HTTP 400

file_too_large

PDF is over the 10MB cap (413 when PUT to the upload route).

HTTP 400 (also 413)

pdf_fetch_failed

pdf_url wasn't publicly fetchable as a PDF.

HTTP 400

pdf_required

This piece format needs a PDF and none was supplied.

HTTP 400

blocked_address

pdf_url points at a private, reserved, or non-http(s) address and was refused.

HTTP 400

dns_failed

pdf_url's host did not resolve.

HTTP 400

redirect_limit

pdf_url redirected too many times.

HTTP 400

invalid_upload_key

upload_key isn't a key this server issued.

HTTP 400

upload_not_found

The upload_key expired or was already consumed.

HTTP 400

unknown_pack

No credit pack with that id.

HTTP 400

invalid_stars

A review was submitted with stars outside 1–5.

HTTP 400

unsupported_mail_class

The rail that prints this piece does not offer that mail class.

HTTP 400

unsupported_piece

The rail does not produce that piece format.

HTTP 400

unknown_format

No such piece format.

HTTP 400

class_unavailable_for_format

That mail class cannot carry that physical piece — a postcard cannot go certified.

HTTP 400

Credential errors — only if you send a key

invalid_key

An Authorization header was sent and did not verify. Omit it entirely to call anonymously.

HTTP 401

key_disabled

The key is real and has been turned off.

HTTP 403

insufficient_scope

Valid key, but it does not carry the scope this route needs (`reason` names what it does carry).

HTTP 403

key_store_unavailable

The key could not be checked, so it is not honoured. A credential we cannot verify is refused, never downgraded.

HTTP 503

Confirmation errors (action_required) — the spend-safety gate

confirmation_required

No confirmation_token on a surface that requires one. Quote first; a send is never the first call.

HTTP 428

confirmation_malformed

Not a paperplane quote token.

HTTP 400

confirmation_invalid

Signature check failed — the token was altered or signed by another deployment.

HTTP 400

confirmation_expired

Past its 30-minute window. Quote again.

HTTP 400

confirmation_mismatch

Valid token, different letter — recipient, class, colour, tracking, content, or price changed since the quote.

HTTP 409

confirmation_used

Already redeemed. **The letter you quoted was sent.** This is replay protection working; do not resend.

HTTP 409

quote_content_required

A quote carrying no letter content cannot mint a token. A token binds a quote to one specific letter, so one bound to no document is not a weaker quote -- it is a blank cheque for any document the send path cannot fingerprint.

HTTP 400

confirmation_unavailable

No signing secret configured, so no token could be minted or checked.

HTTP 503

Capability errors — order-scoped grants

invalid_capability

The token is missing, malformed, or was issued for a different order or action.

HTTP 403

expired_capability

Past its 7-day window. Gone, not forbidden — create a new order for a fresh pair.

HTTP 410

capability_unavailable

No signing secret configured, so capability tokens cannot be issued or checked.

HTTP 503

Payment errors

unknown_code

No credit account with that code (404 from the balance lookup, 402 when used to pay for an order).

HTTP 402 (also 404)

insufficient_balance

The credit code exists but cannot cover this order; `next` states the balance.

HTTP 402

x402_not_configured

Agent payments are off on this deployment. Use /v1/credits/checkout or a credit_code.

HTTP 501

payment_required

No X-PAYMENT header (or MCP `payment_header`) was supplied; the response body is a full x402 payment-required challenge — sign it and resubmit with the header set.

HTTP 402

region_unsupported

The x402 stablecoin lane is hidden in this region (New York) because the underlying Stripe payment method is not offered there; use /v1/credits/checkout or a credit_code.

HTTP 403

x402_verify_failed

The facilitator would not verify the X-PAYMENT payload.

HTTP 402

x402_settle_failed

Verification passed and settlement did not. Nothing was minted.

HTTP 402

x402_payment_invalid

The X-PAYMENT header is not a payload we can read.

HTTP 402

payments_not_configured

This deployment can't take payments right now.

HTTP 503

no_checkout_url

Stripe accepted the session and returned no URL to send the payer to.

HTTP 502

no_client_secret

Stripe accepted an embedded-checkout session (checkout_ui_mode: "embedded") and returned no client_secret to mount.

HTTP 502

State errors — the order is real and isn't in the state you asked about

not_found

No order with that id. Sandbox orders are cleared on redeploy.

HTTP 404

too_late

Already handed to the carrier; the cancellation window is closed.

HTTP 409

not_reviewable

That order is not delivered, or has already been reviewed.

HTTP 409

not_held

A review action on an order that is not held.

HTTP 409

address_undeliverable

USPS verification rejected the recipient address; `reason` says why.

HTTP 422

rejected

The fulfillment rail terminally refused the piece. Not retryable — fix the piece.

HTTP 422

idempotency_key_in_flight

An earlier request with this `Idempotency-Key` has not finished. Retry with **the same key** after `Retry-After` to receive that request's order -- retrying with a new key is the intuitive move and it mails a second letter.

HTTP 409

Limits — back off, not rewrite

rate_limited

Per-IP throttle on a guest endpoint. Honour Retry-After.

HTTP 429

velocity_limit

Per-sender send cap (5/day, 20/month).

HTTP 429

spend_limit

Per-credential spend cap ($50/day, $200/month).

HTTP 429

caller_unidentified

We could not identify the caller well enough to hold it to a cap, so the send is refused rather than uncapped.

HTTP 403

Platform errors — our side, retryable (with two exceptions, marked below)

unavailable

Transient fulfillment failure. Safe to retry.

HTTP 503

rail_unavailable

Valid request, but no rail is currently configured to mail it.

HTTP 503

limit_check_unavailable

The cap ledger was unreachable, so the send is refused rather than run uncapped.

HTTP 503

audit_unavailable

The audit trail was unreachable. We do not mail what we cannot attribute.

HTTP 503

below_cost_floor

This piece cannot be sent at its current price — a rail cost or plan-tier fault on our side, not your request. Nothing was charged; try a different format or mail class, or contact support.

HTTP 503

retry_unsafe

The rail did not confirm the send and cannot be asked, so we refused to retry rather than risk a second physical piece. Nothing was sent and nothing was charged. **Do not auto-retry** — resubmit only if a human has confirmed nothing arrived.

next

  • Do not auto-retry — see the error contract for the safe recovery step.

HTTP 503

submit_outcome_unknown

The rail could not tell us whether the piece reached the print facility, so it was not sent again. Nothing was charged, but a piece may already be in the mail. **Do not auto-retry** — resubmitting could produce a duplicate.

next

  • Do not auto-retry — see the error contract for the safe recovery step.

HTTP 503

internal

Unexpected. Retry once, then report it with the response body.

HTTP 500