---
title: PostalForm MPP endpoint guide
description: Use this file when an MPP registry reviewer or MPP-capable agent needs only PostalForm's MPP endpoints and payment instructions.
seotitle: PostalForm MPP endpoint guide
seo-description: MPP-only instructions for PostalForm machine-payment endpoints, including document mail, bulk mail, postcards, workflow forms, and flower letters.
group: resources
indexable: true
llms: true
nav: false
schema: webpage
eyebrow: MPP
published: 2026-06-17T00:00:00+00:00
updated: 2026-06-19T00:00:00+00:00
path: /mpp
---
# PostalForm MPP endpoint guide

Use this file when an MPP registry reviewer or MPP-capable agent needs only PostalForm's MPP endpoints and payment instructions.

## MPP endpoints
Document mail, workflow forms, postcards, and bulk mail:

| Purpose                                                         | Method and path                         |
| --------------------------------------------------------------- | --------------------------------------- |
| Discovery probe challenge                                       | `GET /api/machine/mpp/orders`           |
| Validate and quote without payment side effects                 | `POST /api/machine/mpp/orders/validate` |
| Create unpaid order, receive challenge, and retry after payment | `POST /api/machine/mpp/orders`          |
| Poll payment and fulfillment status                             | `GET /api/machine/mpp/orders/{id}`      |

Flower letters:

| Purpose                                                                | Method and path                                 |
| ---------------------------------------------------------------------- | ----------------------------------------------- |
| Validate product, ZIP, delivery date, and final quote                  | `POST /api/machine/mpp/flower-letters/validate` |
| Create unpaid flower order, receive challenge, and retry after payment | `POST /api/machine/mpp/flower-letters`          |
| Poll payment and florist submission status                             | `GET /api/machine/mpp/flower-letters/{id}`      |

Flower catalog prep endpoints are not MPP-paid, but agents need them before creating a flower-letter MPP order:

| Purpose                       | Method and path                                                      |
| ----------------------------- | -------------------------------------------------------------------- |
| List flower categories        | `GET /api/flowers/categories`                                        |
| List flower products          | `GET /api/flowers/products?category=bs&count=18&start=1&sorttype=pa` |
| List delivery dates for a ZIP | `GET /api/flowers/delivery-dates?zipcode=32503`                      |

## Payment flow
1. Validate first. Use the relevant `/validate` endpoint and show the quote to the owner.
2. Create the unpaid order with the same JSON body and no `Authorization` header.
3. PostalForm returns HTTP `402` with one or more `WWW-Authenticate: Payment ...` headers.
4. Parse the returned challenges and choose exactly one supported method.
5. Pay the challenge with an MPP-capable client.
6. Retry the exact same create request with the exact same JSON body and `request_id`, adding `Authorization: Payment <serialized-mpp-credential>`.
7. Read the `Payment-Receipt` header on success.
8. Poll the returned status endpoint until `payment_status` is `paid`.

Do not create a second order after the `402`. Reuse the same `request_id` and byte-equivalent JSON body. If the body changes for an existing `request_id`, PostalForm returns `409 request_id_mismatch`.

## Payment methods
Use one of these methods for this MPP verification guide:

- `tempo`: settle the Tempo challenge with an MPP-capable Tempo client such as `mppx`.
- `stripe_spt`: mint or obtain a buyer-approved Stripe Shared Payment Token (`spt_...`) and serialize it into the Stripe MPP credential.

## Document mail quick start
Use `POST /api/machine/mpp/orders/validate`, then `POST /api/machine/mpp/orders`.

Document-mail MPP addresses follow the same country configuration as PostalForm's checkout address picker. Manual `countryCode` values and Loqate ID country prefixes must be one of `US`, `CA`, `AT`, `BE`, `CH`, `DE`, `FR`, `GB`, `IN`, `LU`, or `NL`. Omitted manual-address countries default to `US`; unsupported or invalid codes are rejected. Bulk CSV rows use the same list through an optional `country`, `country_code`, or `countrycode` column.

```bash
curl -sS https://postalform.com/api/machine/mpp/orders/validate \
  -H 'Content-Type: application/json' \
  --json '{
    "request_id": "8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b",
    "buyer_name": "Agent Owner",
    "buyer_email": "owner@example.com",
    "letter": {
      "title": "MPP verification letter",
      "body": "Hello,\n\nThis is a PostalForm MPP verification letter.\n\nSincerely,\nAgent Owner",
      "format": "text",
      "signature": "Agent Owner"
    },
    "sender_name": "Agent Owner",
    "sender_address_type": "Manual",
    "sender_address_manual": {
      "line1": "123 Sender St",
      "city": "Springfield",
      "state": "IL",
      "zip": "62701",
      "countryCode": "US"
    },
    "recipient_name": "Recipient Example",
    "recipient_address_type": "Manual",
    "recipient_address_manual": {
      "line1": "456 Recipient Ave",
      "city": "Springfield",
      "state": "IL",
      "zip": "62701",
      "countryCode": "US"
    },
    "double_sided": true,
    "color": false,
    "mail_class": "standard",
    "certified": false
  }'
```

Create the unpaid order using the same body:

```bash
curl -i https://postalform.com/api/machine/mpp/orders \
  -H 'Content-Type: application/json' \
  --json @mpp-mail-body.json
```

On `402`, choose one returned MPP challenge and retry:

```bash
curl -i https://postalform.com/api/machine/mpp/orders \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Payment <serialized-mpp-credential>' \
  --json @mpp-mail-body.json
```

Poll:

```bash
curl -sS https://postalform.com/api/machine/mpp/orders/8c1a1b58-2c8f-4f4f-9c46-2c1ac32d7a1b
```

See the mail-only version: `https://postalform.com/mpp-mail.md`

## Flower-letter quick start
Use the flower catalog endpoints first, then validate and create with the MPP flower endpoints.

```bash
curl -sS https://postalform.com/api/machine/mpp/flower-letters/validate \
  -H 'Content-Type: application/json' \
  --json '{
    "request_id": "0bc151ff-31a9-47a6-a0a4-2853cd75ff2a",
    "buyer_name": "Agent Owner",
    "buyer_email": "owner@example.com",
    "note": "For your desk, your day, and the smile I hope this brings.",
    "zipcode": "32503",
    "product_code": "F1-509",
    "delivery_date": "2026-07-03",
    "customer": {
      "name": "Agent Owner",
      "phone": "8505550100",
      "address1": "123 Sender St",
      "city": "Pensacola",
      "state": "FL",
      "zipcode": "32503",
      "country": "US"
    },
    "recipient": {
      "name": "Recipient Example",
      "phone": "8505550199",
      "address1": "456 Recipient Ave",
      "city": "Pensacola",
      "state": "FL",
      "zipcode": "32503",
      "country": "US"
    },
    "allow_substitutions": true
  }'
```

Create and retry after payment using the same `POST /api/machine/mpp/flower-letters` body. Poll `GET /api/machine/mpp/flower-letters/{id}`.

See the flower-only version: `https://postalform.com/mpp-flowers.md`

## Stripe SPT credential shape
For Stripe SPT, the retry header must contain the serialized MPP credential, not the bare `spt_...` token.

```ts
import { Challenge, Credential } from 'mppx'

const unpaid = await fetch('https://postalform.com/api/machine/mpp/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(body),
})

if (unpaid.status !== 402) throw new Error(`Expected 402, got ${unpaid.status}`)

const challenges = Challenge.fromResponseList(unpaid)
const stripeChallenge = challenges.find((challenge) => challenge.method === 'stripe')
if (!stripeChallenge) throw new Error('No stripe challenge returned')

const spt = await mintSharedPaymentGrantedToken({
  amount: stripeChallenge.request.amount,
  currency: stripeChallenge.request.currency,
  networkId: stripeChallenge.request.methodDetails?.networkId,
  externalId: body.request_id,
})

const authorization = Credential.serialize(
  Credential.from({
    challenge: stripeChallenge,
    payload: { spt },
  })
)

const paid = await fetch('https://postalform.com/api/machine/mpp/orders', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': authorization,
  },
  body: JSON.stringify(body),
})
```

The token must match the challenge amount, currency, seller network id, and validity window.

## Tempo client shape
If your verifier uses `mppx`, it can parse the `WWW-Authenticate: Payment ...` challenge, settle it, and retry:

```bash
mppx \
  https://postalform.com/api/machine/mpp/orders \
  --account my-tempo-wallet \
  --json-body "$(cat mpp-mail-body.json)" \
  --include \
  --verbose
```

## Status values
Document mail status responses include:

- `awaiting_payment`: unpaid draft exists and can be retried with `Authorization: Payment ...`.
- `settled_pending_webhook`: payment was accepted and PostalForm is waiting for final payment finalization.
- `paid`: payment is finalized and fulfillment has been dispatched or queued.

Flower-letter status responses include:

- `awaiting_payment`: unpaid flower order exists.
- `settled_pending_submission`: payment was accepted and florist submission is queued.
- `paid`: payment was accepted and submission processing has started.

## Errors agents should handle
- `400` or `422 invalid_request`: payload validation failed. Read the `errors` array and fix the request before paying.
- `402 payment_required`: normal MPP challenge response. Pay one challenge and retry the same create request.
- `409 request_id_mismatch`: the same `request_id` was reused with a different payload. Use the original body or a fresh UUID.
- `422 missing_buyer_email`: `buyer_email` is required for machine payments and Stripe receipt delivery.
- `429 rate_limited`: slow down unpaid order creation and retry later.
- `500 payment_error`: payment verification failed or the payment provider returned an error.

## Machine-readable references
- MPP mail guide: `https://postalform.com/mpp-mail.md`
- MPP flower guide: `https://postalform.com/mpp-flowers.md`
- OpenAPI: `https://postalform.com/openapi.json`
- Broader agent guide: `https://postalform.com/agents.md`
- Agent skill: `https://postalform.com/skill.md`
