# Courio: full documentation > Courio (sendcourio.com) is a REST API and MCP server that prints a PDF and mails it as a real postal letter to supported destinations worldwide. No account. Quote for free, get the user to approve and pay (Stripe Checkout link, or by the agent itself: card through MPP / Stripe Shared Payment Token, or x402 USDC on Base), then track the letter. Short version: https://sendcourio.com/llms.txt. Installable skill: https://sendcourio.com/skill.md. Interfaces: REST https://api.sendcourio.com/v1, OpenAPI 3.1 https://api.sendcourio.com/openapi.json, MCP (streamable HTTP, no auth) https://api.sendcourio.com/mcp with tools get_coverage, quote_letter, send_letter, get_letter_status. Over MCP you cannot pay: send_letter returns a Stripe link for the user. ## x402 spend limit Official x402 clients refuse payments above $1 by default. A letter usually costs more: raise the spend limit of your x402 client to at least the quote's `usd_amount`, or it will refuse to pay. ## Flow Courio sends physical letters on behalf of AI agents. No account is needed: an agent quotes, creates, pays and follows a letter with plain HTTPS calls. ## Agent flow 1. **Quote** - `POST /v1/letters/quote` with sender, recipient, a service level and the document (`pdf_base64`, `pdf_url` or `text`). You get a frozen price (EUR, VAT included, plus the USD amount for x402) valid 24 h, and a `quote_id`. Check `GET /v1/coverage` first if unsure a country/service exists. 2. **Create** - `POST /v1/letters` with the `quote_id` and a `payment_method`. - `stripe`: answers **201** with `payment.checkout_url` (card-only Stripe Checkout). A human opens it and pays. - `mpp` (Machine Payments Protocol, card through a Stripe Shared Payment Token; may be disabled on a deployment: **422** `payment_method_unavailable`, use `stripe`): the first call answers **402** with a `WWW-Authenticate: Payment` challenge (method `stripe`, intent `charge`, the EUR price). Get an SPT (`spt_...`) for it from your owner's wallet (e.g. `npx @stripe/link-cli mpp pay`) and resend the *same* request with `Authorization: Payment ` (or `Payment-Authorization` if `Authorization` carries your API key); the answer is **201** with `Payment-Receipt`. If the card issuer asks for 3-D Secure, the answer is 402 `authentication_required`: fall back to `stripe`. - `x402` (may be disabled on a deployment: you then get **422** `payment_method_unavailable`, use `stripe`): the first call without payment answers **402** with the requirements in the `PAYMENT-REQUIRED` header (and the body). Sign a USDC payment and resend the *same* request with the `PAYMENT-SIGNATURE` header; the answer is **201** with `PAYMENT-RESPONSE`. Official x402 clients cap a single payment at $1 by default: raise the spend limit to at least the quote's `usd_amount`, or the client will refuse to pay. 3. **Pay** - the letter is created in `awaiting_payment` (Stripe) or directly `paid` (MPP, x402). 4. **Poll or listen** - `GET /v1/letters/{id}` or receive webhooks (below). Statuses: `awaiting_payment`, `paid`, `submitted`, `in_transit`, `delivered`, `returned`, `failed`, `expired`. **Payment is irreversible once made**: after payment the letter is printed and mailed; it cannot be cancelled or edited. If Courio cannot fulfil it, the payment is refunded automatically (Stripe, MPP) or by hand (x402). Check the document and the addresses before paying. ## Terms Paying for a letter accepts the terms of sale: https://sendcourio.com/terms. Privacy policy: https://sendcourio.com/privacy. Legal notice: https://sendcourio.com/legal. Quote and letter-creation responses repeat `terms_url` and `privacy_url`: show the terms to the human before payment. Every `POST /v1/*` requires an `Idempotency-Key` header. Errors are `application/problem+json` with `type`, `title`, `status`, `code` and a one-sentence `hint` meant for an agent. ## Authentication Optional. Without a key you can quote, create and read a letter you know the id of (redacted view). A `ck_` API key (`Authorization: Bearer ck_...`) attaches letters to your key, gives the full view of your letters, enables `GET /v1/letters` and has its own daily limit. ## Webhooks Pass `webhook_url` (public https) when creating a letter. The creation response returns a `webhook_secret` (only there). Each status change is POSTed as JSON `{event_id, letter_id, status, occurred_at, data}` with the header `Courio-Signature: t=,v1=` where `v1 = HMAC-SHA256(webhook_secret, ".")`. Verify the signature on the raw body and reject old timestamps. Any non-2xx answer is retried with backoff for 24 h; deduplicate on `event_id`. ## MCP The same capabilities are exposed as MCP tools at `/mcp` (streamable HTTP, stateless, POST only). ## Coverage and starting prices From GET https://api.sendcourio.com/v1/coverage at build time (1 page, black and white; the exact price is in the quote): - FR: standard (Lettre verte) from €2.00 VAT incl., 4-5 days; tracked (Lettre suivie) from €11.60 VAT incl., 5-15 days; registered (Lettre recommandée) from €9.20 VAT incl., 3-5 days; registered_ar (Lettre recommandée avec AR) from €25.30 VAT incl., 5-15 days - DE: standard (Standardbrief) from €1.80 VAT incl., 2-4 days; tracked (Einschreiben Einwurf) from €5.50 VAT incl., 2-4 days; registered (Einschreiben) from €19.10 VAT incl., 5-15 days; registered_ar (Einschreiben Rückschein) from €25.30 VAT incl., 5-15 days - AT: standard (Brief) from €2.20 VAT incl., 2-4 days; registered (Einschreiben) from €19.10 VAT incl., 5-15 days; registered_ar (Einschreiben mit Rückschein) from €25.30 VAT incl., 5-15 days - CH: standard (B-Post Brief) from €2.50 VAT incl., 3-4 days; tracked (A-Post Plus) from €11.60 VAT incl., 5-15 days; registered (Einschreiben) from €9.90 VAT incl., 2-3 days; registered_ar (Einschreiben mit Rückschein) from €25.30 VAT incl., 5-15 days - NL: standard (Brief) from €2.60 VAT incl., 2-3 days; registered (Aangetekende brief) from €19.10 VAT incl., 5-15 days; registered_ar (Aangetekend met ontvangstbevestiging) from €25.30 VAT incl., 5-15 days - BE: standard (Lettre non prioritaire) from €2.10 VAT incl., 3-4 days; registered (Envoi recommandé) from €13.50 VAT incl., 2-3 days; registered_ar (Recommandé avec accusé de réception) from €25.30 VAT incl., 5-15 days - LU: standard (Lettre) from €2.30 VAT incl., 2-3 days; registered (Envoi recommandé) from €19.10 VAT incl., 5-15 days; registered_ar (Recommandé avec avis de réception) from €25.30 VAT incl., 5-15 days - GB: standard (Second Class) from €2.40 VAT incl., 3-4 days; tracked (Tracked 48) from €11.60 VAT incl., 5-15 days; registered (Signed For) from €19.10 VAT incl., 5-15 days - ES: standard (Carta ordinaria) from €2.00 VAT incl., 4-6 days; registered (Carta certificada) from €8.10 VAT incl., 3-5 days; registered_ar (Carta certificada con aviso de recibo) from €25.30 VAT incl., 5-15 days - US: standard (First-Class Mail) from €6.60 VAT incl., 5-15 days; tracked (First-Class Mail with tracking) from €11.60 VAT incl., 5-15 days; registered (Certified Mail) from €19.10 VAT incl., 5-15 days; registered_ar (Certified Mail with Return Receipt) from €25.30 VAT incl., 5-15 days - * (every other country): standard (Letter) from €6.60 VAT incl., 5-15 days ## Endpoints ### POST /v1/letters/quote Get a frozen price for a letter. Validates addresses and the document, runs content moderation and routes to a provider. Creates no letter and charges nothing. The quote is valid 24 h. Parameters: - Idempotency-Key (header, required): Unique key (max 255 chars) per logical request. Repeating a request with the same key and body replays the stored response; same key with another body is `idempotency_key_reused`. Body (* = required): service*: string, sender*: object, recipient*: object, document*: one of, options: object, currency: string Example body: ```json { "service": "registered_ar", "sender": { "name": "Acme SAS", "address_lines": [ "12 rue de la Paix" ], "postal_code": "75002", "city": "Paris", "country": "FR", "email": "ops@acme.example" }, "recipient": { "name": "Erika Mustermann", "address_lines": [ "Unter den Linden 1" ], "postal_code": "10117", "city": "Berlin", "country": "DE" }, "document": { "text": "Madame, Monsieur,\n\nPar la présente, nous vous notifions la résiliation du contrat n° 4711.\n\nCordialement" }, "options": { "color": false, "duplex": true }, "currency": "EUR" } ``` Answers: 200, 400, 401, 422, 429, 500, 503 ### POST /v1/letters Create a letter from a quote and pay for it. With `payment_method: "stripe"` answers 201 with a Checkout URL. With `"x402"`, call once without `PAYMENT-SIGNATURE` to get the 402 and its `PAYMENT-REQUIRED` header, then resend the same request with the signed payment. With `"mpp"`, call once without a credential to get the 402 and its `WWW-Authenticate: Payment` challenge, then resend the same request with `Authorization: Payment ` carrying a Shared Payment Token. The 402 (and a 429 `limit_exceeded`) is never stored by the idempotency layer: retry the same request with the same key. Payment is irreversible once made. Parameters: - Idempotency-Key (header, required): Unique key (max 255 chars) per logical request. Repeating a request with the same key and body replays the stored response; same key with another body is `idempotency_key_reused`. - PAYMENT-SIGNATURE (header): x402 only: base64 JSON of the signed x402 v2 PaymentPayload for `accepts[0]` of the 402 response. - Payment-Authorization (header): MPP only, instead of `Authorization: Payment ...` when `Authorization` carries your API key: `Payment ` answering the challenge with `{"spt": "spt_..."}`. Body (* = required): quote_id*: string, payment_method*: string, webhook_url: string Example body: ```json { "quote_id": "qt_7Hn2KdQ4VxLm9RwYb3TcZs5P", "payment_method": "stripe", "webhook_url": "https://agent.example/hooks/courio" } ``` Answers: 201, 400, 401, 402, 404, 409, 410, 422, 429, 500, 503 ### GET /v1/letters List the letters created with your API key. Parameters: - status (query): - limit (query): - cursor (query): `next_cursor` of the previous page. Answers: 200, 400, 401, 500 ### GET /v1/letters/{id} Read a letter and its timeline. Public with the letter id (recipient redacted to city and country, no tracking details beyond status); full view when called with the API key that created it. Proof URLs are signed and short-lived. Parameters: - id (path, required): Answers: 200, 401, 404, 500 ### GET /v1/coverage Countries and services Courio delivers, with starting prices. Parameters: - country (query): Uppercase ISO 3166-1 alpha-2 code. Answers: 200, 400, 500 ## Errors - validation_failed (400: The body, query or path did not validate; `errors` lists the fields.) - idempotency_key_required (400: Idempotency-Key header missing.) - unauthorized (401: Authorization header present but not a valid ck_ API key.) - idempotency_key_reused (422: Idempotency-Key already used with a different body.) - invalid_address (422: An address cannot be used (`field` says which).) - product_unavailable (422: No provider delivers this service to this country.) - document_invalid (422: The PDF/text is unusable (`reason`).) - content_rejected (422: Moderation refused the content.) - amount_limit_exceeded (422: The price exceeds the per-letter cap.) - not_routable (422: No provider could be routed for this letter.) - limit_exceeded (429: Daily limit reached: letters (IP, sender email, wallet or API key) or quotes (IP or API key); retry after `retry_after` seconds with the same request and key.) - internal_error (500: Unexpected server error.) - moderation_unavailable (503: Moderation could not run; nothing was accepted, retry.) - pricing_unavailable (503: No EUR/USD rate available; retry shortly.) - quote_not_found (404: No quote with this id.) - quote_already_used (409: The quote already created a letter; `letter_id` points to it.) - quote_expired (410: The 24 h quote validity elapsed; request a new quote.) - payment_method_unavailable (422: The payment method is disabled on this deployment (x402 or mpp may be off); use `stripe`.) - payment_unavailable (503: Stripe or the x402 facilitator is unavailable; retry the same request (MPP: with the same credential, never charged twice).) - letter_not_found (404: No letter with this id.) ## Pages - https://sendcourio.com/agents - https://sendcourio.com/countries - https://sendcourio.com/pricing - https://sendcourio.com/tracking