Skip to content

Docs

REST and MCP use the same letter service. Connect an MCP assistant below, or use the REST API for a platform such as Muse.ai that calls HTTP endpoints.

REST integrations

Using Muse.ai? Start with the Muse.ai setup guide for a copyable connection prompt and Google sign-in instructions.

Base URL: https://paperagent.dev/v1. Use an existing OAuth access token or a PaperAgent API key in Authorization: Bearer …. When self-service is enabled, connect automatically with the device flow below. Integrations requiring a manual key can use an optional key from your account page. Read and write permissions are shared with MCP.

OpenAPI request and response schemas · Agent instructions

Automatic connection — no support email

Read OAuth discovery first. If device_authorization_endpoint is absent, new-customer sign-in, device connections and key creation are unavailable; stop onboarding without offering an API-key fallback. When advertised, register a public client at /oauth/register with explicit urn:ietf:params:oauth:grant-type:device_code and refresh_token grants, no redirect URI, and letter scopes. POST your client ID, scopes and resource https://paperagent.dev/mcp to /oauth/device_authorization.

Show the returned connection link and code directly to the customer. A host's manual API-key connector is a separate flow and should not replace that link. They sign in with Google, validate and confirm their return address, and approve the agent. Poll /oauth/token with the device grant, client ID and device code every five seconds. Continue on authorization_pending; add five seconds to the interval on slow_down. Stop on denial or ten-minute expiry. Store the resulting tokens securely and rotate the refresh token at /oauth/token when the one-hour access token expires. Disconnect an agent from your account to revoke all its tokens.

A customer with a confirmed return address can create an unpaid order. Confirmed live Stripe payment completes payment verification before live fulfillment. Test keys and test payments never enable live mailing. Browser account management requires Google sign-in and CSRF protection; agents cannot mint extra credentials. The complete JSON examples are in llms.txt.

  1. Create a draft with POST /v1/drafts. Include the recipient and a text or Markdown body; standard is the default tier.
  2. Review GET /v1/drafts/{id}/preview. Show the PDF, mailing requirements and total. Obtain approval before sending. Re-preview after edits.
  3. Call POST /v1/letters with the draft ID, approved confirm_total_cents, and a unique idempotency_key. Reuse the key on retries.
  4. Pay the existing order using its payment_url with a linked wallet, or give the customer the checkout_url.
  5. Follow GET /v1/letters/{id} or subscribe to webhooks. Payment can be followed by review before mailing.

Linked wallet payments

POST an empty object to the payment URL to receive an HTTP 402 MPP challenge. Have the wallet approve it, then retry the same request and body with its credential in Payment-Authorization. Keep your account token in Authorization. The challenge expires after 15 minutes. Success returns a Payment-Receipt and the existing order status. A host holding an approved Stripe shared payment token can instead POST {"shared_payment_token":"…"} to that URL.

Availability depends on the payment configuration for your credential's mode. PaperAgent test keys use test payments. An unavailable or declined payment includes a checkout fallback when available. After a timeout, retry the original token and order; a 409 means payment may still be resolving. Never create a second letter to retry a charge. Muse wallet authorization still needs validation with the platform; the integration tests use mock payments.

MethodEndpointPermission
GET/v1/account/session

Read the Google browser session and CSRF token.

Google session + CSRF for writes
POST/v1/account/logout

Sign out of this browser session.

Google session + CSRF for writes
POST/v1/account/sender/validate

Validate a customer return address and show postal corrections.

Google session + CSRF for writes
POST/v1/account/sender

Confirm the validated address receives the customer’s mail.

Google session + CSRF for writes
GET/v1/account/connections

List connected agents and their unverified labels.

Google session + CSRF for writes
DELETE/v1/account/connections/{id}

Revoke an agent’s entire access and refresh token family.

Google session + CSRF for writes
GET/v1/account/api-keys

List API key metadata; never secrets.

Google session + CSRF for writes
POST/v1/account/api-keys

Create a named test or live key with letter scopes; secret shown once.

Google session + CSRF for writes
DELETE/v1/account/api-keys/{id}

Revoke one customer API key.

Google session + CSRF for writes
GET/v1/pricing

Published prices, available products and limits.

Public
POST/v1/addresses/validate

Validate a US mailing address.

Public
POST/v1/drafts

Create a letter draft and quote. Nothing is printed or charged.

letters:write
PATCH/v1/drafts/{id}

Edit a draft and refresh its quote.

letters:write
GET/v1/drafts/{id}

Retrieve a draft and its stored quote.

letters:read
GET/v1/drafts/{id}/preview

Review the PDF, current total and mailing requirements before approval.

letters:read
POST/v1/letters

Create one order for an approved draft and exact total. Reuse idempotency_key on retries.

letters:write
GET/v1/letters/{id}

Read payment, mailing status, scans and delivery estimates.

letters:read
GET/v1/letters

List letters with cursor pagination.

letters:read
POST/v1/letters/{id}/checkout

Pay the existing letter with a shared payment token. An empty body requests an MPP challenge; use Payment-Authorization for its credential.

letters:write
GET/v1/account

Read the connected account and credential mode.

letters:read
GET/v1/account/notifications

Read email preferences; settings apply across both modes.

letters:read
PATCH/v1/account/notifications

Change one email preference.

letters:write
POST/v1/webhook_endpoints

Subscribe an HTTPS endpoint; the signing secret is returned only once.

letters:write
GET/v1/webhook_endpoints

List webhook subscriptions.

letters:read
GET/v1/webhook_endpoints/{id}

Read a webhook subscription.

letters:read
PATCH/v1/webhook_endpoints/{id}

Update a webhook subscription.

letters:write
DELETE/v1/webhook_endpoints/{id}

Delete a webhook subscription.

letters:write
POST/v1/abuse-reports

Report unwanted mail. Responses do not reveal recipient history.

Public

Address validation defaults to 20 requests/minute before sign-in and 60 per account, with a shared daily provider budget. Abuse reports allow 5/minute per IP. Respect Retry-After on 429. Cancellation and refunds are handled by support.

Connect your agent

One address, every assistant that speaks MCP:

https://paperagent.dev/mcp

get_pricing and validate_address answer without signing in, so you can check a price and an address before connecting anything. Every other tool asks you to sign in with your email the first time it is called.

No platform publishes a link that pre-fills an MCP address, so all three come down to pasting the line above into a settings screen — and each one gates who is allowed to. Here is where it goes, and who it is open to:

ChatGPT

Settings → Connectors → Advanced → Developer mode.

Needs ChatGPT Plus, Pro, Business, Enterprise or Edu, and developer mode has to be switched on from a browser. Once the connector is added it works in the mobile apps too.

How to connect ChatGPT

Claude

+ menu → Add connector → Add custom connector.

Works on every Claude plan. On Team and Enterprise an owner adds it once in organisation settings and everyone else connects from their own Connectors page.

How to connect Claude

Gemini

Connected apps → Custom apps for Spark → Add a custom app.

Google limits custom apps to people who are 18 or over, in the US, signed in with a personal Google Account rather than a work or school one, and who have Keep Activity switched on. The app has to be added from the Gemini web app on a computer; after that it works on your phone as well.

The address above already works here. We are not sending people down this path yet — the guided page goes up once we have walked it on a real Gemini account.

MCP tool reference

Read tools require letters:read; tools that change a draft or prepare payment require letters:write. Reconnect with the required permission if a call is refused. A send requires your approval of the preview and exact total.

get_pricing
Read available mail services, current prices and limits.
Permission: No sign-in required
validate_address
Validate a US postal address before preparing a letter.
Permission: No sign-in required
create_draft
Create a letter draft from the recipient and your text.
Permission: letters:write
update_draft
Revise an existing draft before sending.
Permission: letters:write
preview_draft
Read the PDF preview and current quote for approval.
Permission: letters:read
send_letter
Prepare the approved letter and payment. Reuse the same idempotency key when retrying; a replay returns the existing letter and its current status.
Permission: letters:write
get_letter_status
Read the current status and available postal visibility for a letter.
Permission: letters:read
list_letters
List letters belonging to your account.
Permission: letters:read

By default, address validation is limited to 20 requests per minute before sign-in and 60 per minute per connected account. A shared daily provider budget also applies. Respect Retry-After and the reset time in any refusal.

How it is called

MCP assistants display the service as:

paperagent.send_letter

The tool description reads, in the register a model matches against: Send a real, physical letter through the United States Postal Service — printed on paper, put in an envelope, stamped, and mailed. This is not email.

Constraints, stated up front

  • US domestic addresses only.
  • Mailed within one business day.
  • A letter is at most 3 printed pages. A longer body is refused rather than billed at a higher price — shorten it, or split it into two letters.
  • A plain First-Class letter gets no delivery scan. We show processing visibility and an expected delivery date, and we do not call that tracking.