PostalForm MCP
Mail real letters from agents: PDF → checkout → status.
Documentation
Mail API and Remote MCP Server for AI Agents
PostalForm is a mail API and remote MCP server for turning PDFs, letters, forms, and agent output into real physical postal mail. Developers can create reviewable mail drafts, hosted checkout sessions, direct MPP/x402 machine orders, and fulfillment-status lookups without building print, envelope, postage, or carrier-handoff infrastructure.
Quick answer: can developers send postal mail through an API?
Yes. Use PostalForm Projects when your server needs persistent API keys, a free test environment, final-price quotes, idempotent order creation, and signed webhooks. Use the remote MCP and hosted-checkout draft tools when a person should review the PDF, addresses, mailing options, and price before payment. Use direct MPP or x402 machine-payment flows only for autonomous runtimes with explicit owner approval, spending limits, and idempotent request IDs.
| Developer intent | PostalForm path | Best default |
|---|---|---|
| Add a persistent mail API to an app | Use the PostalForm Projects developer mail API | Start in free test mode with a project API key |
| Let an AI agent prepare physical mail | Connect to the remote MCP endpoint at https://postalform.com/mcp | Create a hosted-checkout draft |
| Let ChatGPT, Claude, Gemini, Cursor, or Codex use mail tools | Register PostalForm as a remote streamable HTTP MCP server | Require approval for mail side effects |
| Let an autonomous runtime pay for mail | Use postalform.create_machine_order, MPP, or x402 | Owner-approved spend limit plus preview review |
| List PostalForm in an MCP or agent registry | Use the MCP server card, registry manifest, and this developer page | Submit the canonical PostalForm-hosted URLs |
Directory-ready summary
Use this table when evaluating PostalForm for an MCP registry, agent directory, API catalog, connector review, or developer-tool listing.
| Directory field | PostalForm value |
|---|---|
| Product type | Physical mail API and remote MCP server |
| Primary endpoint | https://postalform.com/mcp |
| Transport | Streamable HTTP MCP |
| Human-reviewed default | Hosted-checkout draft tools create an unpaid draft and checkout URL for review before payment |
| Payment-capable path | Every PDF, letter, and form draft can select MPP and use postalform.pay_order after approval; postalform.create_machine_order also supports x402 |
| No-auth discovery URLs | /openapi.json, /apis.json, /.well-known/mcp/server.json, /.well-known/mcp/server-card.json, /.well-known/mcp.json, /.well-known/agent-card.json, /.well-known/x402 |
| Best listing destination | https://postalform.com/developers for API/MCP catalogs; https://postalform.com/agents for agent directories |
| Main use cases | Mail PDFs, letters, generated documents, workflow forms, dispute packets, payment notices, and status checks |
| Side-effect boundary | Draft creation does not pay or mail. Payment requires hosted checkout, a Stripe token, or an MPP/x402 credential |
| Recommended approval policy | Require approval before sharing documents, addresses, payment credentials, or sending real mail |
Short directory description:
PostalForm is a remote MCP server and physical mail API for creating reviewable print-and-mail drafts from PDFs, letters, and forms, with hosted checkout, fulfillment status, and approved MPP/x402 machine-payment flows.
Which integration surface should you use?
| Surface | Use it for | Avoid it when |
|---|---|---|
| PostalForm Projects REST API | Server-side apps that need project API keys, free simulated fulfillment, final quotes, prepaid credits, and signed webhooks | A person should approve each document and payment in hosted checkout |
| Hosted checkout via MCP draft tools | User-facing assistants, web chat clients, support tools, and workflows where a person should approve payment | The agent has already been authorized to pay autonomously |
| Stripe token checkout session | Clients that can obtain a buyer-approved Stripe shared payment token | The client cannot use checkout sessions or shared payment tokens |
| Root machine-order REST API | Approved MPP/x402 integrations that validate, pay, retry, and poll without a Projects workspace | The user still needs a visual approval step before payment |
| MPP machine payment | Agent runtimes that already support MPP challenge/receipt semantics, Stripe Shared Payment Tokens, or card-MPP credentials | Simple user-facing flows where hosted checkout is safer |
| x402 machine payment | HTTP-native agents and services that can answer 402 Payment Required challenges | Workflows without wallet/payment infrastructure or spend controls |
| UCP checkout MCP endpoint | Platforms that specifically integrate with the UCP checkout capability | Manual-address, letter-text, or workflow-form drafts that need PostalForm MCP tools |
Domestic parcel shipping labels are a separate MPP-only product. Use POST /api/machine/mpp/shipping-labels/validate for live carrier rates, then POST /api/machine/mpp/shipping-labels to create and pay. The paid response and completion page expose a signed PDF download; the fulfillment email includes the PDF attachment and link. See the MPP shipping-label guide.
Remote MCP discovery
Use these canonical URLs when listing PostalForm in MCP directories, connector catalogs, or agent registries:
- Remote MCP endpoint: https://postalform.com/mcp
- A2A Agent Card: https://postalform.com/.well-known/agent-card.json
- A2A JSON-RPC discovery bridge: https://postalform.com/a2a
- MCP server card: https://postalform.com/.well-known/mcp/server-card.json
- MCP compatibility manifest: https://postalform.com/.well-known/mcp.json
- MCP Registry server manifest: https://postalform.com/.well-known/mcp/server.json
- Agent skill: https://postalform.com/skill.md
- LLM overview: https://postalform.com/llms.txt
- Full LLM content dump: https://postalform.com/llms-full.txt
- Open Knowledge Format index: https://postalform.com/okf/index.md
- APIs.json index: https://postalform.com/apis.json
- API catalog: https://postalform.com/.well-known/api-catalog
- API specification: https://postalform.com/openapi.json
- Agent guide: https://postalform.com/agents
- AI instructions: https://postalform.com/ai
The hosted MCP server is best for clients that can connect to a remote streamable HTTP MCP endpoint. Use postalform.create_order_draft, postalform.create_letter_order_draft, and postalform.create_form_order_draft to prepare an order for buyer review, followed by complete_checkout with a compatible Stripe payment token or payment through the hosted checkout URL. For MPP, those same draft tools accept payment_protocol: "mpp" and buyer_email; use postalform.pay_order to pay the prepared order after approval. postalform.create_machine_order also supports MPP and x402.
For the design pattern behind this split, see making physical mail callable from AI agents.
ChatGPT, Gemini, and other MCP clients
PostalForm is ready for clients that can connect to remote MCP servers over streamable HTTP. The same endpoint is used by ChatGPT custom connector-style flows, OpenAI Responses API MCP tools, Gemini SDK MCP sessions, Gemini CLI, Claude, Claude Code, Codex, Cursor, Windsurf, Cline, Replit, OpenClaw, Hermes, n8n, LangChain, and similar MCP clients.
For ChatGPT or OpenAI Responses API integrations, configure PostalForm as a remote MCP server:
{
"type": "mcp",
"server_label": "postalform",
"server_description": "Create real PostalForm mail drafts, hosted checkout sessions, machine-payment mail orders, and order status lookups.",
"server_url": "https://postalform.com/mcp",
"require_approval": "always"
}
For Gemini CLI, add PostalForm as an HTTP MCP server:
gemini mcp add --transport http postalform https://postalform.com/mcp
For Gemini SDKs, create an MCP client session against the PostalForm endpoint and pass the session tools into Gemini's tool-calling integration. Let the user review the PDF, addresses, price, and mailing options before paying. A compatible client can then use complete_checkout with a buyer-approved Stripe token; otherwise present hosted checkout.
UCP (Universal Commerce Protocol)
PostalForm also supports the UCP Checkout capability for platforms that integrate via the UCP MCP binding.
- Discovery profile: https://postalform.com/.well-known/ucp
- UCP MCP endpoint: https://postalform.com/ucp/mcp
- Capability: dev.ucp.shopping.checkout (version 2026-01-11)
- Tools: create_checkout, get_checkout, update_checkout, complete_checkout, cancel_checkout
UCP checkout sessions are dynamically priced based on PDF page count and print options. PostalForm expects the PDF and address data in metadata.postalform (pdf can be { download_url, file_id }, { upload_token }, a data URL, or a public HTTPS URL).
UCP tool calls must include a platform profile in _meta.ucp.profile.
Note: UCP currently supports PDF-based checkouts with Loqate Address IDs (*_address_type="Address") only. For manual addresses, letter text, and workflow forms, use the PostalForm MCP tools on /mcp.
Example UCP create_checkout:
{
"name": "create_checkout",
"arguments": {
"_meta": {
"ucp": {
"profile": "https://platform.example/profiles/v2026-01/shopping-agent.json"
}
},
"currency": "USD",
"line_items": [{ "item": { "id": "postalform_mail_pdf_bw_double" }, "quantity": 1 }],
"payment": {},
"metadata": {
"postalform": {
"pdf": {
"download_url": "https://example.oaiusercontent.com/file.pdf",
"file_id": "file_abc123"
},
"file_name": "letter.pdf",
"sender_name": "Sender Example",
"sender_address_id": "US|LP|Pz0_Qj4_bGJg|16074807|13_ENG",
"sender_address_type": "Address",
"sender_address_text": "123 Sender St, Springfield, IL 62701",
"recipient_name": "Recipient Example",
"recipient_address_id": "US|LP|Pz0_Qj4_bGJg|199825276|99_ENG",
"recipient_address_type": "Address",
"recipient_address_text": "456 Recipient Ave, Springfield, IL 62701",
"double_sided": true,
"color": false
}
}
}
}
Machine payments (x402)
PostalForm supports x402-based machine payments for direct API order creation. The machine payment endpoints are designed for agent runtimes that may not have a PostalForm login or API key: the unpaid create call returns a payment challenge, and the paid retry authorizes the order. Letter payloads can be raw text, HTML, Markdown, or RTF and can include typed or drawn signatures; PostalForm renders the PDF server-side and may return a signed preview URL before payment.
For a crawler-friendly overview of the x402 physical-mail flow, directory fields, and safety defaults, see x402 physical mail API.
- Create/pay endpoint:
POST https://postalform.com/api/machine/orders - Validate/quote endpoint (no payment side effects):
POST https://postalform.com/api/machine/orders/validate - Status endpoint:
GET https://postalform.com/api/machine/orders/:id - Flow: unpaid request returns
402+PAYMENT-REQUIRED, client pays and retries withPAYMENT-SIGNATURE, server returns202+PAYMENT-RESPONSE;settled_pending_webhookmeans Stripe is still verifying the settlement, so poll the status endpoint rather than paying again
Required request fields for POST /api/machine/orders:
request_id(UUID)buyer_namebuyer_email(required; set on Stripe PaymentIntent asreceipt_email)- exactly one document source:
pdf(recommended canonical format:{ "upload_token": "..." }; also accepts{ download_url, file_id }, data URL, or public HTTPS URL)letter(raw string or object withformatset totext,html,markdown, orrtf; optionalsignatureas a string or{ mode: "typed" | "drawn", text?, dataUrl?, printDataUrl? }; PostalForm renders the PDF server-side)form(single-mailpiece workflow form payload discovered through the machine forms schema endpoints; signature fields accept typed or drawn signature payloads when present; statutory multi-recipient workflows are excluded and remain web-only)
- sender/recipient names + addresses (Loqate:
*_address_type="Address"+*_address_id+*_address_text, or Manual:*_address_type="Manual"+*_address_manualwith{ line1, line2?, city, state?, zip, countryCode? };countryCodedefaults toUS; both address modes accept only the checkout-supported country codesUS,CA,AT,BE,CH,DE,ES,FR,GB,IN,LU, andNL)
Common options:
double_sided(defaulttrue)color(defaultfalse)mail_class(standard,priority,express)certified(defaultfalse)certified_return_receipt(defaultfalse; adds a return receipt for US Certified Mail; ignored for PinGen registered mail)return_receipt_format(electronicby default, orphysicalfor the mailed PS Form 3811 green card; requirescertified_return_receipt=true)restricted_delivery(defaultfalse; requests addressee-only USPS Restricted Delivery and requirescertified_return_receipt=true)err_delivery(manualby default, oremailto have PostalForm acquire and email the signed electronic receipt)err_email(optional override for automatic delivery; defaults tobuyer_emailwhen omitted)signature_required(defaultfalse; requests USPS signature-on-delivery; ignored for postcards or whenmail_class="standard")mailpiece_type(letterorpostcard; defaultletter)postcard_size(4x6,6x9,11x6; required whenmailpiece_type="postcard")
Postcard machine orders use the same endpoints and payment flow. For postcards, send pdf as the final composed postcard PDF and set mailpiece_type: "postcard" plus postcard_size.
Postcard PDF requirements:
- Use a 2-page PDF.
- Page 1 is the artwork side.
- Page 2 is the mailing side. You can place backside artwork or a non-address message there, but do not place sender/recipient names, return or delivery addresses, indicia, or barcode data in the PDF. PostalForm fills the mailing block automatically.
- Match the exact bleed canvas for the selected size. The canonical spec and templates live at postcard PDF guidelines:
- Lob-routed international postcards require
postcard_size="4x6"and a U.S. sender/return address. PostalForm routes larger international postcards or international postcards with non-U.S. return addresses to PostGrid before provider submission.
PostalForm normalizes postcard print options server-side to standard mail, full color, double-sided, and no certified/signature add-ons.
MCP endpoint
- https://postalform.com/mcp
- Methods: POST/GET/DELETE with the streamable HTTP transport
- Sessions: initialize once, then include the
mcp-session-idheader on all subsequent requests - Auth: no authentication is required today (contact support@postalform.com for allowlisting)
- Transport: JSON-RPC payloads over POST
Integration quickstart
- Point your MCP client at /mcp and initialize a session.
- Choose addresses: use Loqate IDs via
postalform.search_addresses, or use manual addresses with*_address_type="Manual"+*_address_manual(countryCodedefaults toUS). - Create a draft:
- Letter text:
postalform.create_letter_order_draft. - Workflow forms:
postalform.list_forms->postalform.get_form_schema->postalform.create_form_order_draft. - PDF upload:
postalform.create_order_draft(file params,upload_token, data URL, or public HTTPS URL).
- Letter text:
- Choose the payment path:
- Compatible Stripe token clients: obtain buyer approval and call
complete_checkoutwithcheckout_session.id, buyer details, and the token. Otherwise send the buyer tocheckout_url. - MPP-capable clients: choose
payment_protocol: "mpp"and supplybuyer_emailwhen creating any draft. Reviewpreview_urlandprice_usd, answer one MPP challenge, then callpostalform.pay_orderwith onlyorder_idandpayment_authorization.create_machine_orderremains available for MPP and x402.
- Compatible Stripe token clients: obtain buyer approval and call
- Poll order status as fulfillment progresses.
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
const transport = new StreamableHTTPClientTransport(new URL('https://postalform.com/mcp'))
const client = new Client({ name: 'my-agent', version: '1.0.0' })
await client.connect(transport)
await client.listTools()
const draft = await client.callTool({
name: 'postalform.create_letter_order_draft',
arguments: {
letter: {
title: 'Demand for payment',
body: 'Hello...\\n\\nThis is my letter body.\\n\\nSincerely,\\n',
signature: 'Sender Example',
},
sender_name: 'Sender Example',
sender_address_type: 'Manual',
sender_address_manual: {
line1: '123 Sender St',
city: 'Springfield',
state: 'IL',
zip: '62701',
},
recipient_name: 'Recipient Example',
recipient_address_type: 'Manual',
recipient_address_manual: {
line1: '456 Recipient Ave',
city: 'Springfield',
state: 'IL',
zip: '62701',
},
},
})
console.log(draft.structuredContent?.checkout_url)
Payment and checkout
External checkout (any MCP client)
- Use
checkout_urlfrompostalform.create_order_draftto send customers to the hosted PostalForm payment page. - Best default for browser-chat MCP clients or any flow where a person should review and pay.
- Open the URL in a browser (or
window.openai.openExternalinside ChatGPT). - Poll
postalform.get_order_statusto confirm payment and fulfillment.
Stripe token checkout (compatible agent clients)
- Draft tools return an ACP
checkout_sessioncontaining the prepared order ID and total. - After the buyer approves the order and total, call
complete_checkoutwithcheckout_session_id, buyer details, and a compatible Stripe Shared Payment Token (spt_...,provider=stripe). No document or address resubmission is needed. - PostalForm confirms the existing PaymentIntent. Retries check its current state and reuse the same intent; confirmations use a stable Stripe idempotency key. If no intent exists, an unconfirmed intent is saved before any payment attempt.
- Retry an interrupted call with the same checkout ID and token. An uncertain result is
not_ready_for_paymentwith an informational message, not a confirmed decline. Do not create another order or obtain a replacement token while the result is uncertain. completedmeans checkout has been accepted, including an authorization awaiting capture. Followpostalform.get_order_statusfor payment and fulfillment; checkout completion does not prove mailing or delivery. Processing payments returnnot_ready_for_payment; additional authentication returnsrequires_3ds; canceled payments returncanceled. A confirmed decline can be retried with a newly authorized token on the same intent.- Client support must be verified: these tools do not establish that a particular assistant supplies the required token or attachment format.
- ChatGPT widgets can use
window.openai.requestCheckout(checkout_session)where available. Clients without compatible token checkout should presentcheckout_url.
Direct machine payment (agentic MCP clients)
- To pay a prepared draft through MPP, set
payment_protocol: "mpp"andbuyer_emailat creation and usepostalform.pay_orderafter approval. Usepostalform.create_machine_orderfor the combined MPP/x402 machine-order interface. - First call it with
payment_protocol="mpp"or"x402"and no payment credential. - For MPP, pay one returned
WWW-Authenticate: Payment ...challenge with Link CLI, the Link MCP server, Tempo, or a card-MPP client such as Visa'smpp-card/client, then retry the same tool arguments withpayment_authorization. - For x402, pay the returned
PAYMENT-REQUIREDchallenge with an x402-compatible wallet/client, then retry the same tool arguments withpayment_signature. - Reuse the same
request_idand order fields on retry.
Tools
postalform.list_forms: List supported single-mailpiece workflow forms available to agents. Coordinated statutory multi-recipient workflows remain web-only.postalform.get_form_schema: Fetch a supported single-mailpiece workflow schema (fields, dependencies, attachments). The machine schema caps inline attachment files and reportsattachments_max_total_size_mb(currently 12 MB combined after base64 decoding). May includecheckout_flowmetadata (homeorforms_order) for PostalForm web routing; MCP clients/agents can treat this as informational.postalform.create_letter_order_draft: Create a draft order from letter text (server renders a PDF).postalform.create_form_order_draft: Create a draft order from a supported single-mailpiece workflow JSON submission; the server fills a form, renders a letter, or assembles required uploaded packet documents according to the discovered schema. Inline workflow attachments are limited to 12 MB combined after base64 decoding even when the browser upload workflow permits larger files.postalform.create_pdf_upload: Create a short-lived PDF upload URL +upload_tokenfor agents that cannot pass file params. Upload the PDF, then callpostalform.create_order_draftwithpdf: { upload_token: "..." }.postalform.search_addresses: Search mailing address suggestions. Input:query(min 3 chars), optionalcountry_code(defaults toUS), optionalcontainer(for drilling into Container results), optionaltarget. Output: address suggestions with ids/text/type/country. If type is Container, call search again withcontainer=idto get Address results for order drafts.postalform.create_order_draft: Create a draft order and receive a checkout URL. Input:pdf(file object{ download_url, file_id }, or{ upload_token }frompostalform.create_pdf_upload, or adata:application/pdf;base64,...URL, or a public HTTPS download URL), sender/recipient names, and either Loqate addresses (*_address_type="Address"+*_address_id) or manual addresses (*_address_type="Manual"+*_address_manual, with optionalcountryCode). Output:order_id,price_usd,checkout_url,checkout_session.postalform.create_machine_order: Create and pay a machine order from a PDF, letter text, or workflow form using direct MPP or x402. Output:payment_requiredchallenge details on the first call, then paid/settled order details after retrying withpayment_authorization(MPP) orpayment_signature(x402). A paid crypto retry can briefly returnsettled_pending_webhook; poll status and do not pay again.complete_checkout: Pay a prepared checkout session with a buyer-approved Stripe payment token. Input:checkout_session_id(order*id),buyer(first_name, last_name optional, email),payment_data.token(Stripe shared payment token,spt*...),provider=stripe. Output: checkout session response with status and order permalink.postalform.get_order_status: Fetch the latest order status details. Input:order_id. Output:found,is_paid,current_step,error, plus stored public mailing status, carrier tracking evidence, and electronic return receipt availability (see the status example below).postalform.ping: Health check for the MCP server. Input: none. Output: ping payload for connectivity checks.
Tool payload examples
postalform.list_forms
Request:
{
"name": "postalform.list_forms",
"arguments": { "q": "IRS", "limit": 10 }
}
postalform.get_form_schema
Request:
{
"name": "postalform.get_form_schema",
"arguments": { "slug": "1099-nec" }
}
Note: the response may include checkout_flow. This is workflow metadata for PostalForm web checkout routing and does not change MCP tool-call sequencing.
postalform.search_addresses
Request:
{
"name": "postalform.search_addresses",
"arguments": {
"target": "recipient",
"query": "123 Main St"
}
}
Response:
{
"structuredContent": {
"view": "address_suggestions",
"target": "recipient",
"query": "123 Main St",
"suggestions": [
{
"id": "US|LP|Pz0_Qj4_bGJg|16074807|13_ENG",
"text": "123 Main St, Springfield, IL 62701",
"type": "Address",
"description": ""
}
]
}
}
postalform.create_letter_order_draft
Request:
{
"name": "postalform.create_letter_order_draft",
"arguments": {
"request_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"letter": {
"title": "Payment demand letter",
"body": "Hello,\\n\\nThis is the letter body.\\n\\nSincerely,\\n",
"signature": "Sender Example"
},
"sender_name": "Sender Example",
"sender_address_type": "Manual",
"sender_address_manual": {
"line1": "123 Sender St",
"city": "Springfield",
"state": "IL",
"zip": "62701"
},
"recipient_name": "Recipient Example",
"recipient_address_type": "Manual",
"recipient_address_manual": {
"line1": "456 Recipient Ave",
"city": "Springfield",
"state": "IL",
"zip": "62701"
}
}
}
postalform.create_form_order_draft
Request:
{
"name": "postalform.create_form_order_draft",
"arguments": {
"request_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"slug": "1099-nec",
"fields": {
"calendar_year": "2025",
"payer_name": "Sender Example LLC",
"payer_street_address": "123 Sender St",
"payer_city_state_zip_phone": "Springfield, IL 62701, 217-555-0100",
"payer_tin": "12-3456789",
"recipient_tin": "123-45-6789",
"recipient_name": "Contractor Example",
"recipient_street_address": "456 Recipient Ave",
"recipient_city_state_zip": "Springfield, IL 62701",
"box_1_nonemployee_compensation": "1250.00"
},
"sender_name": "Sender Example",
"sender_address_type": "Address",
"sender_address_id": "US|LP|Pz0_Qj4_bGJg|16074807|13_ENG",
"sender_address_text": "123 Sender St, Springfield, IL 62701",
"recipient_name": "Contractor Example",
"recipient_address_type": "Manual",
"recipient_address_manual": {
"line1": "456 Recipient Ave",
"city": "Springfield",
"state": "IL",
"zip": "62701"
}
}
}
postalform.create_pdf_upload
Request:
{
"name": "postalform.create_pdf_upload",
"arguments": {
"file_name": "letter.pdf",
"content_type": "application/pdf",
"content_length": 1234567
}
}
Response:
{
"structuredContent": {
"upload_url": "https://postalform.com/api/mcp/pdf-uploads/pfu_...",
"upload_token": "pfu_...",
"expires_at": "2026-01-17T12:00:00Z",
"max_bytes": 104857600,
"required_headers": {}
}
}
Upload the PDF with multipart/form-data using the file field (aliases pdf and pdfFile are also accepted), then pass upload_token to postalform.create_order_draft or postalform.create_machine_order. Let the HTTP client's multipart encoder generate the Content-Type header, including its boundary; do not set a bare Content-Type: multipart/form-data header when using FormData or curl -F.
postalform.create_order_draft
Note: pdf accepts a file object, an upload_token, or a base64 data URL (data:application/pdf;base64,...). A download URL may use any public HTTPS host, including signed attachment URLs from Muse or other clients; it must work without extra authentication headers. Each redirect must also use HTTPS and resolve to public addresses. Private/internal addresses are blocked; downloads are limited to 100 MB, five redirects, and 20 seconds.
Request:
{
"name": "postalform.create_order_draft",
"arguments": {
"request_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"pdf": { "upload_token": "pfu_..." },
"file_name": "letter.pdf",
"sender_name": "Sender Example",
"sender_address_id": "US|LP|Pz0_Qj4_bGJg|16074807|13_ENG",
"sender_address_type": "Address",
"sender_address_text": "123 Sender St, Springfield, IL 62701",
"recipient_name": "Recipient Example",
"recipient_address_id": "US|LP|Pz0_Qj4_bGJg|199825276|99_ENG",
"recipient_address_type": "Address",
"recipient_address_text": "456 Recipient Ave, Springfield, IL 62701",
"double_sided": true,
"color": false
}
}
Response:
{
"structuredContent": {
"view": "order_draft",
"order_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"page_count": 2,
"price_usd": 2.99,
"checkout_url": "https://postalform.com/payment?orderId=8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"sender_name": "Sender Example",
"sender_address_text": "123 Sender St, Springfield, IL 62701",
"recipient_name": "Recipient Example",
"recipient_address_text": "456 Recipient Ave, Springfield, IL 62701",
"checkout_session": {
"id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"payment_provider": {
"provider": "stripe",
"merchant_id": "profile_123",
"supported_payment_methods": ["card", "apple_pay", "google_pay"]
},
"status": "ready_for_payment",
"currency": "usd",
"line_items": [
{
"id": "line_item_8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"item": { "id": "postalform_mail_pdf", "quantity": 1 },
"base_amount": 299,
"discount": 0,
"subtotal": 299,
"tax": 0,
"total": 299
}
],
"totals": [
{ "type": "items_base_amount", "display_text": "Items", "amount": 299 },
{ "type": "subtotal", "display_text": "Subtotal", "amount": 299 },
{ "type": "tax", "display_text": "Tax", "amount": 0 },
{ "type": "total", "display_text": "Total", "amount": 299 }
],
"links": [
{ "type": "terms_of_use", "value": "https://postalform.com/terms" },
{ "type": "privacy_policy", "value": "https://postalform.com/privacy" }
],
"payment_mode": "test"
}
}
}
MPP for any draft
postalform.create_order_draft, postalform.create_letter_order_draft, and postalform.create_form_order_draft accept:
{
"payment_protocol": "mpp",
"buyer_email": "buyer@example.com",
"buyer_name": "Buyer Example"
}
Include those fields alongside the normal PDF, letter, or form arguments and a stable request_id. buyer_email is required for MPP; buyer_name defaults to the sender. The response has payment_protocol: "mpp", checkout_url: null, preview_url, price_usd, and payment containing the order status, payment endpoint, and MPP challenges (payment.payment.www_authenticate). No payment is submitted by draft creation. Omitting payment_protocol preserves hosted checkout and complete_checkout.
After review and buyer approval, answer one returned challenge and pay the same prepared order:
{
"name": "postalform.pay_order",
"arguments": {
"order_id": "11111111-1111-4111-8111-111111111111",
"payment_authorization": "Payment <credential>"
}
}
Omit payment_authorization to refresh the challenge. Link CLI or another MPP HTTP client can POST {} to https://postalform.com/api/machine/mpp/orders/{order_id}/pay. An unpaid call returns HTTP 402 with WWW-Authenticate; retry that same URL and body with Authorization: Payment .... Successful payment returns order status and Payment-Receipt. Reuse the same order ID and credential after an interrupted response. paid and settled_pending_webhook retries do not submit another payment; poll status instead. The document and addresses are never resubmitted. The payment path is selected at draft creation; an existing hosted checkout cannot be changed into an MPP order by calling pay_order.
postalform.create_machine_order
First call for an MPP/Link payment challenge:
{
"name": "postalform.create_machine_order",
"arguments": {
"payment_protocol": "mpp",
"request_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"buyer_name": "Agent Owner",
"buyer_email": "owner@example.com",
"letter": {
"title": "Payment demand letter",
"body": "Hello,\\n\\nThis is the letter body.\\n\\nSincerely,\\n"
},
"sender_name": "Sender Example",
"sender_address_type": "Manual",
"sender_address_manual": {
"line1": "123 Sender St",
"city": "Springfield",
"state": "IL",
"zip": "62701"
},
"recipient_name": "Recipient Example",
"recipient_address_type": "Manual",
"recipient_address_manual": {
"line1": "456 Recipient Ave",
"city": "Springfield",
"state": "IL",
"zip": "62701"
}
}
}
Response shape:
{
"structuredContent": {
"view": "machine_order",
"protocol": "mpp",
"order_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"status": "payment_required",
"payment": {
"www_authenticate": ["Payment ..."],
"retry_header": "Authorization",
"retry_header_value": "Payment ..."
}
}
}
After Link CLI or Link MCP returns the paid MPP credential, retry the same arguments with:
{
"payment_authorization": "Payment ..."
}
For x402, set payment_protocol to x402; the first response contains payment.payment_required_header, and the retry uses payment_signature.
complete_checkout
Request:
{
"name": "complete_checkout",
"arguments": {
"checkout_session_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"buyer": {
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com"
},
"payment_data": {
"token": "spt_test_123",
"provider": "stripe"
}
}
}
Response:
{
"structuredContent": {
"id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"buyer": {
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com"
},
"status": "completed",
"currency": "usd",
"line_items": [
{
"id": "line_item_8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"item": { "id": "postalform_mail_pdf", "quantity": 1 },
"base_amount": 299,
"discount": 0,
"subtotal": 299,
"tax": 0,
"total": 299
}
],
"fulfillment_options": [
{
"type": "shipping",
"id": "postalform_standard",
"title": "First Class mail",
"subtitle": "Mailed via postal carrier",
"carrier": "Postal carrier",
"carrier_info": "PostalForm print-and-mail provider",
"earliest_delivery_time": "2026-07-01T00:00:00.000Z",
"latest_delivery_time": "2026-07-06T00:00:00.000Z",
"subtotal": 0,
"tax": 0,
"total": 0
}
],
"fulfillment_option_id": "postalform_standard",
"totals": [
{ "type": "items_base_amount", "display_text": "Items", "amount": 299 },
{ "type": "subtotal", "display_text": "Subtotal", "amount": 299 },
{ "type": "tax", "display_text": "Tax", "amount": 0 },
{ "type": "total", "display_text": "Total", "amount": 299 }
],
"order": {
"id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"checkout_session_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"permalink_url": "https://postalform.com/order/complete?order_id=8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b"
},
"messages": [],
"links": [
{ "type": "terms_of_use", "value": "https://postalform.com/terms" },
{ "type": "privacy_policy", "value": "https://postalform.com/privacy" }
]
}
}
postalform.get_order_status
Response:
{
"structuredContent": {
"view": "order_status",
"found": true,
"order_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
"is_paid": true,
"current_step": "letter_created",
"mailing_status": "processed",
"mailing_status_normalized": "processed_for_delivery",
"tracking_number": "9400000000000000000000",
"carrier": "USPS",
"delivery_status": "delivered",
"delivery_status_detail": "Delivered to authorized agent",
"signed_by": "Authorized agent",
"tracking_events": [],
"estimated_delivery_at": null,
"tracking_updated_at": "2026-09-05T12:00:00Z",
"electronic_return_receipt": {
"requested": true,
"status": "manual",
"document_available": false,
"emailed_at": null
}
}
}
Status reads use the same stored public mailing evidence as the order completion page. mailing_status / mailing_status_normalized describe fulfillment; delivery_status and tracking_events report available carrier evidence. An internal step such as letter_created, or a tracking number alone, does not prove carrier acceptance or delivery. Missing fields are null, missing events are [], and unknown orders return found=false without mailing fields. These fields also support non-US carriers; do not assume every tracking number belongs to USPS.
tracking_events contains at most the latest 50 stored events, with status, statusNormalized, statusDetail, message, occurredAt, and location (postalCode, city, state, label). Events are sanitized like the completion page. tracking_updated_at is the last recorded provider webhook timestamp, not a guarantee of a fresh carrier query. Estimates can change.
electronic_return_receipt.requested means an electronic receipt was selected. Its status is manual, pending, sent, or failed (or null when not requested); sent and emailed_at refer to PostalForm emailing the receipt. document_available means PostalForm has a stored receipt document. A false value does not prove USPS has not issued a receipt, especially with manual retrieval. Physical return receipts do not count as electronic receipts. This tool does not download/acquire receipts or expose their storage paths, and polling never charges, pays, or starts mailing.
Error response example
{
"isError": true,
"content": [
{
"type": "text",
"text": "recipient_address_manual is required when recipient_address_type is Manual."
}
]
}
Integration notes
- Idempotency: use
request_idon draft creation tools (postalform.create_order_draft,postalform.create_letter_order_draft,postalform.create_form_order_draft) and onpostalform.create_machine_order. Reuse the same value and same order fields on retries. The server will return the existing draft/order. - Errors: tool calls that fail validation return
isError=truewith a human-readable message. For token checkout,complete_checkoutcan also return ACP messages with error codes likepayment_declinedorrequires_3ds. - Instant Checkout fallback: if
window.openai.requestCheckoutis unavailable orcheckout_session.payment_providerlacksmerchant_id, usecheckout_urlinstead. - Order status steps:
payment_received,receiver_address_verified,sender_address_verified,pdf_normalized,letter_created,email_sent,canceled,refunded,abandoned.
Limits and requirements
- All orders are printed from PDFs. For
postalform.create_order_draftyou supply the PDF. Forpostalform.create_letter_order_draftandpostalform.create_form_order_draftPostalForm generates the PDF server-side. PDFs are sanitized before printing. - Inline
form.attachmentsare base64 JSON and may total at most 12 MB after decoding. Follow each attachment'sallowed_types,max_files, and machine-cappedmax_size_mbvalues from the workflow schema. - For a workflow with
recipient.mode="user", the top-level recipient name/address controls the envelope. Keep it aligned with recipient address-role fields inform.fields; those fields do not override the top-level machine-order address. - Max 199 pages and 100 MB per file.
download_urlaccepts any public HTTPS host and signed query parameters, with no vendor allowlist. The URL and every redirect must resolve to public addresses and work without extra auth headers. If the client cannot expose a downloadable URL, usepostalform.create_pdf_uploadfor anupload_tokenor adata:application/pdf;base64,...URL. For generated letters, pass text directly topostalform.create_letter_order_draft; no upload is needed.- Address countries default to
USwhen omitted. Use Loqate address ids returned bypostalform.search_addresses(the id prefix carries the country), or use manual address input with*_address_type="Manual"and*_address_manual.countryCode. Machine orders enforce the same country list as the checkout address picker:US,CA,AT,BE,CH,DE,ES,FR,GB,IN,LU, andNL; unsupported or invalid codes are rejected. Bulk CSVs accept the same list throughcountry,country_code, orcountrycode. - Address suggestions can include
type="Container"(buildings/complexes). Callpostalform.search_addressesagain withcontainer=<id>and a refined query (suite, unit, PO Box) to gettype="Address"results. Onlytype="Address"(not Container) is valid for Loqate-based order drafts.
MCP safety, permissions, and rate limits
Treat PostalForm as a real-world action tool because it can create mail drafts and, in machine-payment flows, place paid mail orders after an owner-approved payment challenge. In chat clients, configure the server with require_approval: "always" and require human review before sending user documents, addresses, or payment credentials.
| Tool or flow | Scope | Side effects and limits |
|---|---|---|
postalform.list_forms and postalform.get_form_schema | Reads the published form catalog and schema metadata | No order, file upload, payment, or mail side effect |
postalform.search_addresses | Looks up address suggestions for sender or recipient fields | Calls the address-search provider and is IP rate limited. Defaults are 120 searches per minute and 3,000 per day. It does not create an order or payment |
postalform.create_pdf_upload | Creates a short-lived upload URL for a PDF | Upload token only. The file still has to be attached to a later draft call. Uploads are limited by the documented page and file-size limits |
postalform.create_order_draft | Creates a PDF draft for hosted checkout or MPP | Creates an unpaid draft, returning hosted checkout or an MPP challenge. No printing or mailing occurs before payment |
postalform.create_letter_order_draft | Renders letter text into a draft for hosted checkout or MPP | Creates an unpaid draft, returning hosted checkout or an MPP challenge. No printing or mailing occurs before payment |
postalform.create_form_order_draft | Fills a workflow form for hosted checkout or MPP | Creates an unpaid draft, returning hosted checkout or an MPP challenge. No printing or mailing occurs before payment |
postalform.pay_order | Returns an MPP challenge or pays a prepared MPP order | Pass only order_id and the buyer-approved payment_authorization; reuse that ID on retries. |
postalform.create_machine_order | Creates or pays a direct MPP/x402 machine order | Use only in authorized autonomous runtimes with spend controls. The unpaid call returns a payment challenge; the paid retry must include the MPP authorization or x402 payment signature |
complete_checkout | Pays a prepared checkout with a compatible Stripe token | Use only after the buyer confirms checkout with a Stripe shared payment token |
postalform.get_order_status and postalform.ping | Reads order status or health state | No order creation, payment, or mailing side effect |
Draft-creating tools and direct machine-order tools are also limited by unpaid-order throttling. The default unpaid-order creation limit is 10 attempts per hour per client IP. On rate limits, MCP tools return an error response and HTTP wrapper calls may return 429.
Security scanners should treat hosted checkout as the default safe path: the tool can create a draft, but the customer still reviews the PDF, addresses, price, and mailing options before payment. Direct machine payment should stay behind explicit owner approval, spending limits, idempotent request_id values, and a preview review step when preview_url is present.
Protocol and platform sources
- OpenAI remote MCP and connectors
- Model Context Protocol documentation
- Google Gemini MCP tools
- Stripe Machine Payments Protocol
- x402 payment protocol
FAQs
- Is PostalForm a mail API? Yes. PostalForm exposes machine-order REST endpoints, an OpenAPI catalog, and a remote MCP server for creating physical-mail drafts, quotes, machine-payment orders, and fulfillment-status lookups.
- Can an MCP server send real postal mail? Yes, but PostalForm separates draft creation from paid mailing. Draft tools prepare unpaid orders for hosted checkout or optional MPP payment by order ID. Payments require buyer approval and the appropriate credential.
- What is the safest default for ChatGPT, Claude, Gemini, Cursor, or Codex? Use the remote MCP endpoint at https://postalform.com/mcp, configure approval for mail side effects, create a hosted-checkout draft, and let the user review the PDF, addresses, price, and options before payment.
- Can an agent pay for mail with x402 or MPP? Yes. PostalForm supports direct machine-payment flows for authorized autonomous runtimes. The unpaid call returns a payment challenge, and the paid retry must include the x402 payment signature or MPP authorization.
- Do I need a PostalForm API key? Not for the current public MCP endpoint or machine-payment flow. Some high-volume, abuse-sensitive, or allowlisted deployments may require coordination with support@postalform.com.
- Can I upload PDFs through the API or MCP? Yes. Use the PDF upload tool to create a short-lived upload token, pass a public HTTPS download URL, use a ChatGPT attachment URL, or provide a data URL where supported.
- How do I keep mail side effects safe? Treat physical mail as a real-world action. Use hosted checkout by default, require human approval, set spend limits, reuse idempotency request IDs, and review preview URLs before payment when present.