# PostalMuse agent guide Send US postcards and US Letter documents using a predictable draft/commit REST API. Base URL: https://postalmuse.com/v1 API version: v1 Live sending: disabled; this deployment is sandbox-only ## Discover and qualify - [Capabilities](https://postalmuse.com/v1/capabilities): authoritative enabled products and limits. - [OpenAPI](https://postalmuse.com/openapi.json): request and response schemas. - [Templates](https://postalmuse.com/v1/templates): IDs, versions, and variables. - [PDF letter guide](https://postalmuse.com/docs/letters): private uploads and document limits. US 4x6 postcards and US Letter PDFs/text. No international, scheduling, checks, or Certified Mail on this release. Asset limit 10 MB; letter source limit 20 pages. Templates use Latin characters; upload a print-ready PDF for other scripts. A quote is integer USD cents and expires in 30 minutes. Sandbox prices are illustrative. ## Authenticate POST /sessions with mode=sandbox. No signup/card. Send the returned token as Authorization: Bearer TOKEN. Anonymous sessions expire after 24 hours. Live email sign-in recovers customer history. Sandbox accounts remain confined to their browser/session token. Test and live accounts are isolated. Keep tokens private. ## Happy path 1. Create draft with purpose, to/from addresses, and versioned template or uploaded asset. 2. Poll the same draft until ready or needs_changes. Use poll_after_ms. Stop bounded polling after 30 attempts and resume later. 3. Inspect the proof PDF, page count, address warnings, and price. Sandbox verification is simulated, not real deliverability. 4. POST /drafts/{id}/checkout and give checkout_url to the customer; payment authorizes the immutable revision. Or use /send with an existing funded, scoped grant. Discovering this service never grants spending authority. 5. Save the mailing ID and use GET /mailings/{id} and /events. Submission is not postal delivery. Commit payload: draft_revision, quote_id, manifest_hash, max_total_cents, currency=USD. Mutations require an Idempotency-Key of 8–128 characters. Reuse the same key and body on retry; a new logical action needs a new key. Do not resubmit uncertain mail under a different key. GET is safe to retry. ## Working sandbox example Run in Bash with curl and jq. Example addresses are sandbox fixtures, not live recipients. ```bash # Requires curl and jq. This is sandbox-only; no card is needed. set -eu BASE='https://postalmuse.com' KEY="$(date +%s)-$RANDOM" SESSION=$(curl --fail-with-body -sS "$BASE/v1/sessions" \ -H 'Content-Type: application/json' -H "Idempotency-Key: session-$KEY" \ -d '{"mode":"sandbox"}') TOKEN=$(printf '%s' "$SESSION" | jq -r .token) DRAFT=$(curl --fail-with-body -sS "$BASE/v1/drafts" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -H "Idempotency-Key: draft-$KEY" \ -d '{"kind":"postcard","purpose":"operational","to":{"name":"Sandbox Recipient","address_line1":"100 Example Lane","address_city":"San Francisco","address_state":"CA","address_zip":"94107","address_country":"US"},"from":{"name":"Sandbox Sender","address_line1":"200 Sample Street","address_city":"San Francisco","address_state":"CA","address_zip":"94107","address_country":"US"},"content":{"type":"template","template_id":"everyday_note","template_version":"1","variables":{"title":"Thanks for everything.","message":"A little note to say thank you. It means a lot."}}}') ID=$(printf '%s' "$DRAFT" | jq -r .id) for attempt in $(seq 1 30); do DRAFT=$(curl --fail-with-body -sS "$BASE/v1/drafts/$ID" -H "Authorization: Bearer $TOKEN") STATUS=$(printf '%s' "$DRAFT" | jq -r .status) [ "$STATUS" = ready ] && break [ "$STATUS" = needs_changes ] && { printf '%s\n' "$DRAFT"; exit 1; } sleep 1 done [ "$STATUS" = ready ] || { echo 'Still preparing. Poll this same draft later.'; exit 1; } # Open this PDF and inspect it before committing: printf '%s' "$DRAFT" | jq -r .proof.pdf_url COMMIT=$(printf '%s' "$DRAFT" | jq '{draft_revision:.revision,quote_id:.quote.id,manifest_hash:.manifest_hash,max_total_cents:.quote.total_cents,currency:"USD"}') # Sandbox simulation only. For live purchases, use /checkout instead. curl --fail-with-body -sS "$BASE/v1/drafts/$ID/send" \ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ -H "Idempotency-Key: send-$KEY" -d "$COMMIT" # Save the returned mailing ID; GET /v1/mailings/{id} for status. # Retry an operation with the SAME key and payload. Don't create a replacement. ``` ## Pricing GET /pricing returns the public rate card and availability. Draft quotes are authoritative. The letter launch base includes one document page and its address page; extra document pages and large envelopes are itemized. Applicable tax is added before approval. Never infer a quote from a homepage starting price. ## Files and errors POST /assets using multipart field file, with bearer token and Idempotency-Key. Use asset_id with content.type=pdf for letters. Each letter must use 8.5x11 inch pages. A mailing address page is added. Files stay private; do not host customer files publicly. For invalid_request, document_overflow, or address_needs_review: correct the indicated field. For needs_changes, revise using PATCH and expected_revision. For quote_expired: refresh quote and inspect the new total. For 429: respect Retry-After. For provider_pending: poll the existing operation. For insufficient_funds or customer_approval_required: return the customer action, don't circumvent it. POST /mailings/{id}/cancel requests cancellation. Wait for cancellation=confirmed; eligibility may change. Signed proof URLs expire; GET the draft to renew them. Ordinary mail has incomplete tracking. Provider postal processing can update an address; exact-destination guarantees are not offered.