# Premier Letters — full agent reference

> Premier Letters writes your letters, cards and postcards with a pen and mails them for you — from one thank-you note to a 10,000-piece campaign.

## Positioning

Real pen. Real paper. Real stamp.

- **It gets opened.** Direct mail is read far more often than email, per ANA and USPS research.
- **It gets remembered.** Pen on paper, a hand-addressed envelope, a real Forever stamp — never printed indicia.
- **It is effortless.** Write it like an email. We handle the pen, the paper, the stamp, and the mailbox.
- **Honest pricing.** One all-in price per piece. Longer letters cost more because the pen writes every character.
- **Automate it.** API, webhooks, Gmail, and AI agents through MCP.

## FAQ

**Is it actually handwritten?** Yes — a robotically-driven pen physically writes every letter, card and envelope. Nothing is printed except optional printed-mailpiece add-ons a customer explicitly chooses.

**How much does it cost?** From $4.60 per letter plus postage (We Mail It), postage passed through at face value. The first 300 characters are included; every additional 100 characters adds $0.75 per piece. Paper quality, delivery option (we mail it vs. ship to you) and volume all affect the final price — see /openapi.json or POST /api/v1/validate for an exact quote.

**How long does it take?** We write and mail within 2 business days; USPS First-Class delivery then typically takes 3–5 business days. Letters travel with a real Forever stamp, exactly like personal mail, so there is no tracking number. We tell you the day it goes into the mailbox and the window it should arrive in.

**Can an AI agent place an order on a user's behalf?** Yes, via the MCP server or the REST API below. Without a key, an agent can quote prices, list handwriting styles, verify an address and connect the user's account (connect_account, check_connection, get_pricing, list_handwriting_styles, verify_address). Previewing a letter and creating an order need the user's Premier Letters API key, which the connect flow below obtains with one email approval.

## For AI assistants: connect in three calls

You do not need the user to copy an API key by hand.

1. **connect_account** (MCP, no key) or `POST /api/v1/agent/connect` with `{ "email", "agent_name" }`. We email the account owner an approval link (they can sign up from it). Show the user the returned `user_code`; the approval page shows the same code.
2. The user approves in the email. Meanwhile call **check_connection** (or `GET /api/v1/agent/connect/{request_id}?secret=<poll_secret>`) every 10-30 seconds. It returns `pending`, `denied` or `expired`, or, exactly once, `approved` with the `api_key`. Save the key.
3. **create_order** (or `POST /api/v1/orders`) with `Authorization: Bearer <api_key>`.

Unapproved requests expire after 24 hours; an approved key must be claimed within 1 hour. Pass `mode: "test"` to get a `pl_test_` key instead.

## Placing a correct order

Recommended flow: get_pricing → preview_letter → create_order (with an `idempotency_key` and a `return_address`) → get_order_status.

### Products, cards and long messages

`options.product_type` is `letter` (default), `card` (folded card) or `postcard` (no envelope). Folded cards take two top-level fields: `card_orientation` (`portrait` default | `landscape`) and `card_write_mode` (`standard` default | `reverse` | `full`, which writes the whole surface for $0.50/recipient more). A message longer than one sheet is written across several sheets automatically; POST /api/v1/preview returns `page_count` so you can tell the user before ordering, and pricing scales with the character meter either way.

### Return address

Every order with an envelope needs a return address. Pass `return_address` (`name`, `address_line1`, optional `address_line2` and `company`, `city`, two-letter `state`, `zip`); if you omit it, the account's default return address from Settings is used. With neither, the order is refused with 400 `no_return_address_unacknowledged` unless you set `no_return_address_ack: true` (then undeliverable mail is destroyed). An explicit `no_return_address_ack: true` always wins; otherwise a supplied `return_address`, else the account default. The response says which was used: `return_address.source` is `request`, `profile` or `none`. To send an incomplete address as written (a name alone, say), set `return_address_partial_ok: true`; only the filled lines print.

### Printed output

`options.output_mode` is `handwritten` (default), `printed_bw` or `printed_color`. A printed body replaces the pen-written page with a printed sheet ($0.49 black & white, $0.99 color, no character meter); the envelope stays hand-addressed. `options.envelope_output_mode` (`null` default, `printed_bw` or `printed_color`) prints the envelope instead, adding $0.49 / $0.99 per envelope. get_pricing accepts the same two fields for a quote. `options.mailing_option` is honoured as sent; omit it to use the account's default.

### Idempotency

Send `idempotency_key` (1-255 characters, in the body or as the `Idempotency-Key` header) on every create_order / `POST /api/v1/orders`. Retrying with the same key and the same body returns the original order (HTTP 200, `idempotent_replay: true`) instead of creating and charging a second one. The same key with a different body is a 422 `idempotency_key_reused`; a retry while the first request is still running is a 409 `idempotency_in_progress` (wait and retry).

### Test keys

Keys starting `pl_test_` run the same validation, pricing and return-address checks as live (`pl_live_`) keys but never create, charge or send anything: create_order answers `{ "test": true, "order_id": "test_..." }`, order lists are empty, balance reads 0, and top-ups, new-card charges and Growth record writes answer 400 `test_mode`. Test keys CAN manage webhook endpoints, and those endpoints are account-wide: they receive live order events for the whole account (test orders send none), so register test endpoints carefully.

### Balance

Pay with `payment.method: "balance"` to draw on the prepaid balance; the small-order handling fee is waived, and a short balance is a 402 `insufficient_balance` (nothing created or charged). get_balance (`GET /api/v1/balance`) returns `balance_cents` and the top-up rules; create_topup_link (`POST /api/v1/balance/topup` with `amount_cents` between 1000 and 6000000, i.e. $10.00 to $60000.00) returns a Stripe `checkout_url` for the user to pay; the credit (plus any bonus) lands when they pay. Orders under 5 pieces carry a $0.99 per-piece handling fee, waived when paid from a prepaid balance.

## REST API summary

Base URL: `https://www.premierletters.com/api/v1`. Full machine-readable spec: [/openapi.json](https://www.premierletters.com/openapi.json).

| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/config | Public pricing tiers, char meter, service levels |
| POST | /api/v1/agent/connect | Ask the account owner for a key by email (no key needed) |
| GET | /api/v1/agent/connect/{request_id} | Poll a connect request; returns the key once |
| GET | /api/v1/auth/verify | Verify an API key |
| GET | /api/v1/styles | List handwriting styles |
| POST | /api/v1/validate | Dry-run price + validation for an order |
| POST | /api/v1/preview | Render a letter to SVG + price + page_count, no order created |
| GET/POST | /api/v1/orders | List / create orders (Idempotency-Key header supported) |
| GET | /api/v1/orders/{id} | Order detail |
| GET | /api/v1/orders/{id}/tracking | Per-recipient tracking |
| GET | /api/v1/balance | Prepaid balance + top-up rules |
| POST | /api/v1/balance/topup | Stripe Checkout link that adds balance |
| GET/POST | /api/v1/webhooks | List / create webhook endpoints |

## MCP server

Endpoint: `https://www.premierletters.com/api/mcp` (Streamable HTTP, stateless JSON-RPC 2.0, no SSE). Auth: `Authorization: Bearer pl_live_...` (or `pl_test_...`). `initialize`, `ping` and `tools/list` work without a key; `tools/call` requires one except for: connect_account, check_connection, get_pricing, list_handwriting_styles, verify_address.

### Tools

- **connect_account** (no key required) — Get a Premier Letters API key without the user copying one by hand. Pass the user's email and your name; we email them a link to approve (they can sign up from it if they have no account). Then call check_connection with the returned request_id and poll_secret every 10-30 seconds until it returns the api_key (returned once). Show the returned user_code to the user so they can match it on the approval page. No account or key needed to call this.
- **check_connection** (no key required) — Check whether the user approved a connect_account request. Returns status pending, denied, expired or claimed, or, exactly once, status approved with the api_key. Poll every 10-30 seconds.
- **get_pricing** (no key required) — Get a live price quote for a Premier Letters order. Pass char_count (or letter_count for a rough estimate) plus optional mailing_option and paper_type to get a per-piece and total price, in cents. No account needed. Orders under 5 pieces carry a $0.99 per-piece handling fee (included in total_cents), waived when paid from a prepaid balance. Pass output_mode (printed_bw / printed_color) to quote a printed letter body instead of a pen-written one, and envelope_output_mode to quote a printed envelope.
- **list_handwriting_styles** (no key required) — List available handwriting styles (fonts) for a letter order. Use the returned "id" as options.handwriting_style / handwriting_style when creating an order or previewing.
- **list_templates** (requires API key) — List the authenticated user's saved letter templates.
- **verify_address** (no key required) — Verify and standardize a US mailing address via USPS. Use before create_order to catch typos and get the USPS-standardized form.
- **preview_letter** (requires API key) — Render a preview of a handwritten letter (as an SVG image) and get its price, before creating an order. Does not save anything.
- **create_order** (requires API key) — Create and pay for a Premier Letters order — a real, handwritten letter mailed via USPS. Requires a Premier Letters API key. Use get_pricing or preview_letter first to confirm the price. For payment, "card_on_file" charges the caller's saved card immediately, "balance" pays from the prepaid account balance (no small-order handling fee; fails with insufficient_balance when short), or "checkout_session" returns a checkout_url for the human to complete payment in a browser. Envelope return address: an explicit no_return_address_ack: true always wins; otherwise a supplied return_address, else the account default. With neither address, set no_return_address_ack: true to send without one.
- **get_order_status** (requires API key) — Get an order's status, delivery/tracking summary, and per-recipient mail status.
- **list_orders** (requires API key) — List the authenticated user's recent orders.
- **get_balance** (requires API key) — Get the authenticated account's prepaid balance (balance_cents) and the top-up rules: minimum and maximum top-up, bonus tiers and suggested amounts. Orders paid with payment.method "balance" skip the small-order handling fee. Use create_topup_link to add funds.
- **create_topup_link** (requires API key) — Create a Stripe Checkout link that adds prepaid credit (plus any bonus tier) to the authenticated account. Give checkout_url to the human to pay in a browser; the balance updates once they pay (check with get_balance). amount_cents must be a whole number between min_cents and max_cents from get_balance.

## Add to an MCP client

**Claude:** claude.ai → Settings → Connectors → Add custom connector → URL `https://www.premierletters.com/api/mcp` → paste your Premier Letters API key as the bearer token (or connect without one and let the assistant call connect_account).
**ChatGPT:** Settings → Connectors (developer mode) → Add MCP server → URL `https://www.premierletters.com/api/mcp`.

## More

- [Pricing](https://www.premierletters.com/pricing)
- [FAQ](https://www.premierletters.com/faq)
- [How it works](https://www.premierletters.com/how-it-works)
- [Developers](https://www.premierletters.com/developers)
- [OpenAPI spec](https://www.premierletters.com/openapi.json)
