Mail by PonyDocs

Reference

API reference

View as Markdown

Base URL: https://www.mailbypony.com/api/v1. The machine-readable contract is /api/v1/openapi.json.

Each endpoint lists its MCP tool name and the scope it needs. HTTP and MCP accept the same fields. Over HTTP, send the idempotency key in the Idempotency-Key header; over MCP, send it as the idempotency_key argument.

Errors use one format for every endpoint. See Errors.

Group Endpoints
Account GET /catalog, GET /identity, GET /address-references
Drafts GET /drafts, POST /drafts, GET /drafts/{id}, PATCH /drafts/{id}, attachments, artwork, archive
Proofs POST /drafts/{id}/proofs, GET /proofs/{id}
Credits GET /wallet, GET /wallet/transactions, POST /wallet/topups, GET /wallet/topups/{id}
Sending GET /agent-grants, POST /send-quotes, POST /orders
Orders GET /orders, GET /orders/{id}, POST /orders/{id}/cancel
Templates GET /templates, POST /templates, GET /templates/{id}, DELETE /templates/{id}, POST /templates/{id}/drafts
Batches POST /batches, GET /batches/{id}
Webhooks GET /webhooks, POST /webhooks, DELETE /webhooks/{id}, deliveries, test
Requests GET /requests/{operation}/{key}
Test mode POST /test/credits, POST /test/orders/{id}/advance

Account#

GET /catalog#

catalog · No authentication

Returns the options and limits you can use: papers, envelopes, styles, pricing, limits and prepaid. Use the id values it returns when creating drafts.

prepaid includes minimum_topup_minor, maximum_topup_minor, quote_lifetime_seconds and environment.

GET /identity#

get_connection_identity · letters:read

Returns the credential's own client_id, credential_type (oauth or api_key), scopes and environment (live or test). An assistant's client_id is what you enter in Let my assistant send letters.

GET /address-references#

list_address_references · addresses:read or letters:read

Lists addresses you've confirmed on the website in the last 30 days.

Each item has id, role (sender or recipient), masked (city, state and country), draft_id and expires_at. Use id as a draft's sender_id or recipient_id.


Drafts#

A draft response includes draft_id, version, title, content_type, style, paper, envelope, masked addresses, address_validation, attachments and edit_url.

GET /drafts#

list_drafts · letters:read

Lists your saved drafts as items.

POST /drafts#

create_draft · letters:write · Idempotency key required

Creates a draft. Nothing is ordered or charged.

Field Type Notes
content_policy_acknowledged boolean, required Must be true: confirms the letter is ordinary correspondence
title string Up to 150 characters
content_type string text (default), markdown or pdf
text string Up to 30,000 characters
style string A styles ID from the catalog. Default serif.
paper string A papers ID. Default classic.
envelope string An envelopes ID. Default white.
recipient, sender object A US address: name, line1, line2, city, state, postal_code, country
recipient_id, sender_id string A confirmed address reference, instead of a full address

For a PDF letter, create the draft with content_type: "pdf", then upload the file.

GET /drafts/{draft_id}#

get_draft · letters:read

Returns one draft, including its text and pages.

PATCH /drafts/{draft_id}#

update_draft · letters:write · Idempotency key required

Edits a draft. Send the current version and only the fields you're changing; it accepts the same fields as POST /drafts. Returns the draft with a new version. If the draft changed since you read it, you get VERSION_CONFLICT.

Any edit makes earlier proofs and quotes stale.

POST /drafts/{draft_id}/attachments#

upload_attachment · letters:write · Idempotency key required

Uploads a print-ready PDF, a photo or PNG artwork. Returns the draft with a new version.

Field Type Notes
version integer, required The draft's current version
kind string, required pdf, photo or decoration
name string, required The file name
content_base64 string, required The file, base64-encoded. Up to the catalog's max_upload_bytes (10 MB).
content_policy_acknowledged boolean, required Must be true

PDFs print exactly as uploaded. They must be US Letter, 1 to 10 pages and flattened: no forms, scripts, attachments or links. Upload the original file rather than extracting its text.

DELETE /drafts/{draft_id}/attachments/{file_id}#

remove_attachment · letters:write · Idempotency key required

Removes an attachment and any artwork placed from it. Body: {"version": 3}.

POST /drafts/{draft_id}/artwork#

add_library_artwork · letters:write · Idempotency key required

Adds decorative artwork from Pony's library. Body: version and artwork_id.

POST /drafts/{draft_id}/archive#

archive_draft · letters:write · Idempotency key required

Archives a draft. You can restore it on the website. Body: {"version": 3}.


Proofs#

POST /drafts/{draft_id}/proofs#

preview · letters:read · Idempotency key required

Renders the exact pages and envelope for a draft version. Body: {"version": 1}.

Returns proof_id, status (ready), version, artifact_hash, errors and review_url, a link to view the proof on the website. Rendering finishes before the response returns. If errors isn't empty (for example, a missing ZIP code), fix the draft and render again.

GET /proofs/{proof_id}#

get_proof · letters:read

Returns a proof you've rendered. Proofs never change.


Credits#

GET /wallet#

get_credit_balance · wallet:read

Returns balance_minor, available_minor, currency, status, as_of and environment. Spend only what available_minor shows.

GET /wallet/transactions#

list_credit_transactions · wallet:read

Lists credit history, newest first.

Query Notes
limit 1 to 100. Default 50.
cursor The next_cursor from the previous page

Each item has id, amount_minor (negative for spending), currency, type, created_at and related order or top-up IDs. next_cursor is null on the last page.

POST /wallet/topups#

create_credit_topup · wallet:topup · Idempotency key required

Creates a Stripe Checkout link to add credits. It never charges a card on its own; a person has to pay at the link. Not available to test keys.

JSON
{"amount_minor":2500,"currency":"USD"}

Returns topup_id, status, checkout_url, expires_at, amount_minor and expected_credit_minor.

GET /wallet/topups/{topup_id}#

get_credit_topup · wallet:read

Returns a top-up's status: pending, credited, failed or expired. If you lost the response to POST /wallet/topups, this finds the original Checkout session rather than creating a new one.


Sending#

GET /agent-grants#

list_agent_grants · grants:read

Lists the sending permissions that apply to this credential. Each includes its limits, active, remaining_daily_minor, remaining_lifetime_minor, remaining_orders and next_reset_at.

POST /send-quotes#

quote_for_send · orders:send · Idempotency key required

Gets the final price for one proof.

Field Type Notes
draft_id string, required
version integer, required The proof's draft version
proof_id string, required
client_reference string, required Your identifier for this mailing
send_intent_id string Include when re-quoting a mailing you haven't sent

Returns quote_id, send_intent_id, proof_id, proof_hash, subtotal_minor, tax_minor, total_minor, currency and expires_at. The quote expires after catalog.prepaid.quote_lifetime_seconds.

POST /orders#

send_with_credits · orders:send and an active sending permission · Idempotency key required

Sends a letter. Pony checks the permission and your balance, deducts the total and queues the letter, all in one step.

Field Type Notes
send_intent_id string, required From the quote
quote_id string, required From the quote
proof_id string, required From the quote
expected_total_minor integer, required Must equal the quote's total_minor
currency string, required USD
grant_id string The sending permission to use. Leave it out to use your key's or assistant's newest active permission.

Returns order_id, send_intent_id, status (queued), payment_method (credits), debited_minor, currency, ledger_transaction_id and status_url.

Save the request body and idempotency key before sending. If the response is lost, look it up instead of sending again.


Orders#

An order includes order_id, status, client_reference, send_intent_id, created_at, mailed_at, the masked draft and a history of status changes. See order statuses.

GET /orders#

list_orders · orders:read

Lists your orders as items. Filter with ?client_reference=….

GET /orders/{order_id}#

get_order · orders:read

Returns one order and its current status.

POST /orders/{order_id}/cancel#

cancel_order · orders:cancel · Idempotency key required

Cancels an order before preparation starts. Body: {"approved":true} after explicit cancellation approval. The order's sending permission must allow cancellation.

Returns order_id, status (cancelled), refund_status, credited_minor and currency. The credits return to your balance once, even if you repeat the request. After preparation starts, you get CANCELLATION_TOO_LATE.


Templates#

A template is a reusable letter with {{placeholders}}. See Templates for the placeholder rules.

Request MCP Scope Notes
POST /templates create_template letters:write Idempotency key required. Takes name, title, content_type (text or markdown), text or pages, style, paper, envelope, an optional default sender and content_policy_acknowledged. Returns template_id and the variables it uses.
GET /templates list_templates letters:read
GET /templates/{template_id} get_template letters:read
DELETE /templates/{template_id} delete_template letters:write Idempotency key required. Drafts and orders made from it don't change.
POST /templates/{template_id}/drafts create_template_draft letters:write Idempotency key required. Takes variables, a recipient or recipient_id, and optionally sender, sender_id or title. Returns an ordinary draft.

Batches#

POST /batches#

create_batch · letters:write and orders:send · Idempotency key required

Sends up to 100 letters from your balance in one request. Each item has a unique client_reference and either a template_id with variables, or a draft_id. Each item is drafted, proofed, quoted and sent on its own, and one item's failure doesn't stop the others.

Returns batch_id, status, a summary (requested, queued, failed, pending, debited_minor) and a result for each item. Allow about a second per item. See Batch sends for the full request and retry rules.

GET /batches/{batch_id}#

get_batch · orders:read

Returns a batch's stored result.


Webhooks#

HTTP only. Scope webhooks:write. These routes don't take an idempotency key. See Webhooks.

Request Notes
POST /webhooks Takes url, and optionally events and description. Returns the endpoint with its secret, shown once.
GET /webhooks Lists endpoints, without secrets
DELETE /webhooks/{endpoint_id} Removes an endpoint and cancels its pending deliveries
GET /webhooks/{endpoint_id}/deliveries The 50 most recent deliveries
POST /webhooks/{endpoint_id}/test Sends a signed ping

Requests#

GET /requests/{operation}/{key}#

request_status · The scope of the original request

Looks up the result of an earlier write. operation is the original request's MCP tool name, such as send_with_credits, and key is its idempotency key.

Returns state (committed, rejected or processing) with the original result or error. Returns 404 REQUEST_NOT_FOUND if Pony never recorded the request. See Recovering from a lost response.


Test mode#

These endpoints work only with a pony_test_ key. See Test mode.

Request Scope Notes
POST /test/credits wallet:read Idempotency key required. Adds up to $1,000 of test credit per call: {"amount_minor": 50000, "currency": "USD"}
POST /test/orders/{order_id}/advance orders:read Moves a test order forward one step, or up to the status you send