Skip to content
Back to PostalForm

Developers

PostalForm MCP Developer Docs

PostalForm runs a streamable HTTP MCP server that lets agents create draft mail orders, take payment, and track fulfillment.

How it works

Step 1

Connect and initialize

Use streamable HTTP at /mcp and keep the mcp-session-id header.

Step 2

Create a draft

Search addresses, attach a PDF, and generate a priced order draft with checkout options.

Step 3

Take payment and track

Open checkout_url for external payment or use Instant Checkout in ChatGPT, then poll status.

Agentic Commerce Protocol (ACP) / MCP

PostalForm supports the Stripe Agentic Commerce Protocol via our MCP server at /mcp. This is the ChatGPT Apps SDK-compatible flow that returns a checkout_session for Instant Checkout or a checkout_url for hosted checkout.

MCP endpoint: https://postalform.com/mcp

Integration quickstart

PostalForm runs a streamable HTTP MCP server. Use the official MCP SDK so session management is handled for you.

MCP endpoint: https://postalform.com/mcp
  1. Point your MCP client at /mcp and initialize a session.
  2. Search sender and recipient addresses to capture address ids and types (drill into Container results with container=id).
  3. Create an order draft with the PDF and address ids.
  4. Send the customer to checkout_url, or use checkout_session for ChatGPT Instant Checkout.
  5. 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_order_draft',
  arguments: {
    pdf: { download_url: '<chatgpt-download-url>', file_id: '<file-id>' },
    sender_name: 'Sender Example',
    sender_address_id: '<address-id>',
    sender_address_type: 'Address',
    sender_address_text: '123 Sender St, Springfield, IL 62701',
    recipient_name: 'Recipient Example',
    recipient_address_id: '<address-id>',
    recipient_address_type: 'Address',
    recipient_address_text: '456 Recipient Ave, Springfield, IL 62701',
  },
})

console.log(draft.structuredContent?.checkout_url)

Connection details

  • Endpoint: POST/GET/DELETE /mcp with the streamable HTTP transport.
  • Full MCP URL: https://postalform.com/mcp
  • Base URL: https://postalform.com
  • Sessions: Initialize once, then include the mcp-session-id header on all subsequent requests.
  • Auth: No authentication is required today. Contact us if you need allowlisting.
  • Transport: Streamable HTTP only; JSON-RPC payloads over POST.

Payment and checkout

External checkout (any MCP client)

Use checkout_url from postalform.create_order_draft to send customers to the hosted PostalForm payment page. This works everywhere and does not require ChatGPT-specific payment tokens.

  • Best default for non-ChatGPT MCP clients.
  • Open the URL in a browser (or window.openai.openExternal inside ChatGPT).
  • Poll postalform.get_order_status to confirm payment and fulfillment.

ChatGPT Instant Checkout (Stripe Agentic Commerce)

PostalForm returns an ACP checkout_session in the draft response that ChatGPT uses to show Instant Checkout. When the buyer confirms, ChatGPT calls complete_checkout with a Stripe Shared Payment Token (spt_...).

  • Designed for ChatGPT Apps SDK flows.
  • Use window.openai.requestCheckout(checkout_session) in your UI.
  • complete_checkout processes the shared payment token with Stripe.

Stripe also documents a Stripe-hosted checkout path for agentic apps; PostalForm uses a hosted checkout on its own domain instead. See Stripe Agentic Commerce: Accept a payment for the general framework.

Tool payload examples

Requests below use MCP callTool payloads. Responses show structuredContent snapshots you should parse in your client.

postalform.search_addresses

Use Address results only. If you see Container types, call again with container=id.

{
  "name": "postalform.search_addresses",
  "arguments": {
    "target": "recipient",
    "query": "123 Main St"
  }
}
{
  "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_pdf_upload

Use this when you cannot pass ChatGPT file params. Upload the PDF via multipart, then pass the upload_token to postalform.create_order_draft.

{
  "name": "postalform.create_pdf_upload",
  "arguments": {
    "file_name": "letter.pdf",
    "content_type": "application/pdf",
    "content_length": 1234567
  }
}
{
  "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": {
      "Content-Type": "multipart/form-data"
    }
  }
}

postalform.create_order_draft

Provide sender/recipient names and Loqate Address ids. Reuse request_id to retry safely.

{
  "name": "postalform.create_order_draft",
  "arguments": {
    "request_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
    "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
  }
}
{
  "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"
    }
  }
}

complete_checkout

Only for ChatGPT Instant Checkout. payment_data.token must be a Stripe shared payment token (spt_...).

{
  "name": "complete_checkout",
  "arguments": {
    "checkout_session_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
    "buyer": {
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "[email protected]"
    },
    "payment_data": {
      "token": "spt_test_123",
      "provider": "stripe"
    }
  }
}
{
  "structuredContent": {
    "id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
    "buyer": {
      "first_name": "Jane",
      "last_name": "Doe",
      "email": "[email protected]"
    },
    "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": "Standard mail",
        "subtitle": "Mailed via USPS",
        "carrier": "USPS",
        "carrier_info": "USPS",
        "earliest_delivery_time": "2025-01-01T00:00:00.000Z",
        "latest_delivery_time": "2025-01-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

Poll after payment to track fulfillment progress.

{
  "structuredContent": {
    "view": "order_status",
    "found": true,
    "order_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
    "is_paid": true,
    "current_step": "letter_created"
  }
}

Integration notes

  • Idempotency: Use request_id on postalform.create_order_draft and reuse the same value on retries. The server will return the existing draft.
  • Errors: Tool calls that fail validation return isError=true with a human-readable message. For Instant Checkout, complete_checkout can also return ACP messages with error codes like payment_declined or requires_3ds.
  • Instant Checkout fallback: If window.openai.requestCheckout is unavailable or checkout_session.payment_provider lacks merchant_id, use checkout_url instead.
  • Order status steps: payment_received, receiver_address_verified, sender_address_verified, pdf_normalized, letter_created, email_sent, canceled, refunded, abandoned.

Example error response:

{
  "isError": true,
  "content": [
    { "type": "text", "text": "Recipient address must be a Loqate Address." }
  ]
}

Limits and requirements

  • PDF only; files must be valid PDFs and are sanitized before printing.
  • Max 199 pages and 100 MB per file.
  • download_url must be HTTPS and hosted on an allowlisted domain (ChatGPT attachments use oaiusercontent.com by default). The URL must be publicly reachable without auth headers, use postalform.create_pdf_upload to get an upload_token, or use a data:application/pdf;base64 URL instead.
  • US addresses only; use address ids returned by postalform.search_addresses.
  • Address suggestions can include type="Container" (buildings/complexes). Call postalform.search_addresses again with container=<id> and a refined query (suite, unit, PO Box) to get type="Address" results. Pass sender_address_type and recipient_address_type from the search results; only type="Address" is valid for postalform.create_order_draft.

Tool details

postalform.create_pdf_upload

Create a short-lived PDF upload URL + upload_token.

Input: file_name, content_type, content_length, optional request_id for idempotency. Upload the PDF with multipart/form-data (file field) to upload_url, then call postalform.create_order_draft with pdf: { upload_token: "..." }.

postalform.search_addresses

Search US address suggestions.

Input: query (min 3 chars), optional container (for drilling into Container results), optional target. Output: address suggestions with ids/text/type. If type is Container, call search again with container=id to get Address results for order drafts.

postalform.create_order_draft

Create a draft order and receive a checkout URL.

Input: pdf (ChatGPT file object { download_url, file_id }, or { upload_token } from postalform.create_pdf_upload, or data:application/pdf;base64,..., or an https URL on an allowlisted host), sender/recipient names, address ids/text, sender_address_type + recipient_address_type, optional file_name, double_sided (defaults true), color (defaults false), and request_id for idempotency. Output: order_id, price_usd, checkout_url, checkout_session.

complete_checkout

Finalize a ChatGPT Instant Checkout session with a 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 (for example: payment_received, receiver_address_verified, sender_address_verified, pdf_normalized, letter_created, email_sent, canceled, refunded, abandoned).

postalform.ping

Health check for the MCP server.

Input: none. Output: ping payload for connectivity checks.

Universal Commerce Protocol (UCP)

PostalForm supports the UCP Checkout capability for platforms that integrate via UCP. The discovery profile advertises our UCP MCP endpoint and payment handler configuration. UCP checkouts are dynamically priced based on PDF page count and print options. Include the platform profile in _meta.ucp.profile with each tool call.

Profile: https://postalform.com/.well-known/ucp
UCP MCP: https://postalform.com/ucp/mcp
{
  "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"
        },
        "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
      }
    }
  }
}

What you get

  • postalform.create_pdf_upload
  • postalform.search_addresses
  • postalform.create_order_draft
  • complete_checkout
  • postalform.get_order_status
  • postalform.ping

Usage-based billing

Orders start at a low per-letter rate and are billed when checkout completes. You can preview pricing before you charge.

What happens to your document

Secure handling

Your PDF is stored securely and used only to print and mail the letter you requested.

Retention and deletion

We keep files only as long as needed for fulfillment, then delete them on a rolling schedule.

Support

Need help or a custom workflow? Email [email protected].

FAQs

Do I need authentication?
Not currently. Initialize a session and include the mcp-session-id header on requests.
Which checkout path should I use?
Use checkout_url for standard browser checkout. Use complete_checkout only when ChatGPT provides a Stripe shared payment token.
Is this aligned with Stripe Agentic Commerce?
Yes. PostalForm returns ACP checkout sessions and processes Stripe shared payment tokens for ChatGPT Instant Checkout.
Do you support UCP?
Yes. PostalForm publishes a UCP profile at /.well-known/ucp and exposes a UCP MCP endpoint at /ucp/mcp for create/update/complete/cancel checkout sessions.
What limits apply to PDFs?
PDF only, up to 199 pages and 100 MB, via HTTPS download URLs on allowlisted hosts or base64 data URLs.

Ready to send it?

Start by uploading a PDF and we will guide you through the mailing steps.