v1 APITest and live modes

PieterPost API docs

Create compose links, hosted checkout links, wallet-funded direct orders, temporary uploads, and order lookups from your backend. These docs cover the API surface only.

Base URL

https://pieterpost.com

Create requests

Send JSON for orders and multipart form-data for uploads.

Quick start

The fastest end-to-end test is a test key, a test wallet top-up, one direct-send request, and a status lookup.

Step 1

Create a test key

Sign in to /api/dashboard, create a pp_test_... key, and keep it server-side.

Step 2

Add test wallet credits

Use the dashboard button or call POST /v1/credits/topups. Test top-ups mark paid immediately.

Step 3

Send a direct test order

Call POST /v1/orders with requestType: "letter" or requestType: "postcard".

Step 4

Read the result

Store the returned orderId, then inspect GET /v1/orders/:orderId for the latest status.

Add test credits
Add credits with a test key. Test mode applies the top-up immediately.
Request
curl -X POST https://pieterpost.com/v1/credits/topups \
  -H "Authorization: Bearer pp_test_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: first-five-minutes-topup" \
  -d '{
  "amountCents": 2500,
  "currency": "eur",
  "metadata": {
    "source": "billing-settings"
  },
  "returnUrl": "https://example.com/topups/return"
}'
Response
{
  "amountCents": 2500,
  "checkoutUrl": null,
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "paidAt": "2026-04-21T10:00:01.000Z",
  "status": "paid",
  "testMode": true,
  "topupId": "topup_123",
  "updatedAt": "2026-04-21T10:00:01.000Z",
  "wallet": {
    "balanceCents": 2500,
    "currency": "eur",
    "mode": "test"
  }
}
Send a test letter
Use those credits to create a direct-send letter with POST /v1/orders.
Request
curl -X POST https://pieterpost.com/v1/orders \
  -H "Authorization: Bearer pp_test_your_key_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: first-five-minutes-direct-send" \
  -d '{
  "letters": [
    {
      "message": "This direct-send letter is paid from wallet credits.",
      "recipient": {
        "city": "Springfield",
        "country": "United States",
        "name": "Pieter Post",
        "postalCode": "62704",
        "state": "IL",
        "streetAddress": "123 Main Street"
      }
    }
  ],
  "locale": "en",
  "metadata": {
    "campaign": "spring"
  },
  "requestType": "letter",
  "senderEmail": "sender@example.com"
}'
Response
{
  "amountCents": 200,
  "billingMode": "credits",
  "checkoutUrl": null,
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": null,
  "externalId": null,
  "fulfilledAt": "2026-04-21T10:00:05.000Z",
  "metadata": {
    "campaign": "spring"
  },
  "orderId": "ord_789",
  "orderRef": "7d5118b8-7a91-4e7a-8b92-c72bd46c8432",
  "paidAt": "2026-04-21T10:00:02.000Z",
  "paymentReference": "wallet:ledger_123",
  "recipientCount": 1,
  "requestType": "letter",
  "senderEmail": "sender@example.com",
  "status": "fulfilled",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:05.000Z",
  "wallet": {
    "balanceCents": 2300,
    "currency": "eur",
    "mode": "test"
  }
}
Check the order
Store the returned orderId and read it back when your application needs the latest status.
Request
curl https://pieterpost.com/v1/orders/ord_789 \
  -H "Authorization: Bearer pp_test_your_key_here"
Response
{
  "amountCents": 200,
  "billingMode": "credits",
  "checkoutUrl": null,
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": null,
  "externalId": null,
  "fulfilledAt": "2026-04-21T10:00:05.000Z",
  "metadata": {
    "campaign": "spring"
  },
  "orderId": "ord_789",
  "orderRef": "7d5118b8-7a91-4e7a-8b92-c72bd46c8432",
  "paidAt": "2026-04-21T10:00:02.000Z",
  "paymentReference": "wallet:ledger_123",
  "recipientCount": 1,
  "requestType": "letter",
  "senderEmail": "sender@example.com",
  "status": "fulfilled",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:05.000Z",
  "wallet": {
    "balanceCents": 2300,
    "currency": "eur",
    "mode": "test"
  }
}

Authentication

All v1 endpoints use bearer-token authentication. Keep API keys on your server and use idempotency keys for JSON create requests.

Headers
Authorization: Bearer pp_test_your_key_here
Content-Type: application/json
Idempotency-Key: order-demo-001
Test keys
Keys that start with pp_test_ use sandbox checkout, simulated sending, and test wallet credits.
Live keys
Keys that start with pp_live_ can create live hosted checkout links, add live credits, and send live mail.
Idempotency
Use a stable Idempotency-Key for order, checkout, compose-link, and top-up creates. Uploads do not use idempotency keys.

Modes

Pick the payment and fulfillment mode that matches the product flow you want to ship first.

Test keys
pp_test_... keys use sandbox checkout, test credits, and simulated fulfillment. They do not send live mail.
Live hosted checkout
pp_live_... keys create real hosted checkout links. PieterPost sends after payment succeeds. EUR checkout links offer both card and iDEAL by default. Use paymentMethod: "card" or paymentMethod: "ideal" only when you want to force one method.
Live direct send
Direct orders use wallet credits. Live top-ups return a Stripe checkout URL, then /v1/orders sends live mail after the wallet has enough balance.
Test wallet
A pp_test_... key uses a separate test wallet. Add credits in /api/dashboard or through POST /v1/credits/topups.
Live wallet
A pp_live_... key can create hosted checkout links. Live top-ups return a Stripe checkout URL.
Direct send
Live direct send uses the wallet balance from GET /v1/wallet; no separate approval step is required.

Recipients and payloads

Letter and postcard requests share the same recipient shape, so you can reuse address data across flows.

Required fields
name, postalCode, city, and country are expected. Provide either streetAddress or street plus number.
Optional fields
state, extra, addressLines, and customFields are accepted. Some destinations require state.
Multiple recipients
Letters use a letters array. Postcards use postcard.recipient for one recipient or postcard.recipients for a batch.
Postcard front image
Postcards can use the default front image or an uploaded custom one. Custom postcard fronts must be square, with a 1:1 aspect ratio. To use a custom front, upload a postcard-image first and reuse that asset as postcard.frontImageAssetId or postcard.frontImage.
Template variables
For bulk letters, send top-level composeMode: "template", a shared templateMessage, optional variableKeys, and per-recipient customFields. For postcards, use postcard.composeMode: "template", postcard.message, postcard.variableKeys, and recipient customFields. The API resolves placeholders for each recipient.
Hosted checkout payment
Checkout links default to paymentMethod: "auto". For EUR letter or postcard checkout links, auto offers both card and iDEAL in Stripe Checkout. Pass paymentMethod: "card" or paymentMethod: "ideal" only when you want to force one method.
Letter attachments
Each letter can include uploaded letter-attachment assets. Supported files are PDF, PNG, and JPEG. Destination page limits apply to the combined message and attachment pages.
Letter stamps
Upload letter-stamp-image assets and pass stampImageAssetId to use one custom stamp image for the letter order.
Letter markets
US and non-US letter recipients cannot be combined in one letter order.
Limits
Letter and postcard requests support up to 25 recipients. Letter messages are trimmed to 6,000 characters. Postcard messages are trimmed to 400 characters.
Recipient object
{
  "city": "Springfield",
  "country": "United States",
  "name": "Pieter Post",
  "postalCode": "62704",
  "state": "IL",
  "streetAddress": "123 Main Street"
}

Assets

Use temporary uploads when a postcard needs a square 1:1 custom front image, a letter needs image/PDF attachments, or a letter order needs a custom stamp image.

Upload a postcard front image
Upload a square 1:1 postcard-image as PNG or JPEG, then pass the returned assetId as postcard.frontImageAssetId when you want a custom front.
Request
curl -X POST https://pieterpost.com/v1/uploads \
  -H "Authorization: Bearer pp_test_your_key_here" \
  -F "kind=postcard-image" \
  -F "file=@front.jpg"
Response
{
  "assetId": "asset_123",
  "contentType": "image/jpeg",
  "kind": "postcard-image",
  "name": "asset_123.jpg",
  "originalName": "front.jpg",
  "publicUrl": "https://pieterpost.com/api/public-assets/asset_123",
  "size": 385421
}
Upload a letter attachment
Upload a letter-attachment as PDF, PNG, or JPEG, then pass the returned assetId in a letter attachments array.
Request
curl -X POST https://pieterpost.com/v1/uploads \
  -H "Authorization: Bearer pp_test_your_key_here" \
  -F "kind=letter-attachment" \
  -F "file=@receipt.pdf"
Response
{
  "assetId": "asset_letter_pdf_123",
  "contentType": "application/pdf",
  "kind": "letter-attachment",
  "name": "receipt.pdf",
  "originalName": "receipt.pdf",
  "publicUrl": null,
  "size": 148024
}
Upload a letter stamp image
Upload a letter-stamp-image as PNG or JPEG, then pass the returned assetId as stampImageAssetId.
Request
curl -X POST https://pieterpost.com/v1/uploads \
  -H "Authorization: Bearer pp_test_your_key_here" \
  -F "kind=letter-stamp-image" \
  -F "file=@stamp.png"
Response
{
  "assetId": "asset_stamp_123",
  "contentType": "image/png",
  "kind": "letter-stamp-image",
  "name": "stamp.png",
  "originalName": "stamp.png",
  "publicUrl": null,
  "size": 42192
}
Use an attachment in a letter order
Letter attachments stay private and are validated server-side before pricing.
Request
{
  "externalId": "invoice_attachments_123",
  "letters": [
    {
      "attachments": [
        "asset_letter_pdf_123"
      ],
      "message": "Thanks for your order. The receipt PDF is enclosed.",
      "recipient": {
        "city": "Springfield",
        "country": "United States",
        "name": "Pieter Post",
        "postalCode": "62704",
        "state": "IL",
        "streetAddress": "123 Main Street"
      }
    }
  ],
  "locale": "en",
  "requestType": "letter",
  "returnUrl": "https://example.com/pieterpost-return",
  "senderEmail": "sender@example.com"
}
Response
{
  "amountCents": 250,
  "billingMode": "hosted_checkout",
  "checkoutUrl": "https://pieterpost.com/v1/sandbox/checkout/ord_458",
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": "2026-04-22T10:00:00.000Z",
  "externalId": "invoice_attachments_123",
  "fulfilledAt": null,
  "metadata": {},
  "orderId": "ord_458",
  "orderRef": "48c62242-5a2d-45a2-b073-7df1a8f64a36",
  "paidAt": null,
  "paymentReference": null,
  "recipientCount": 1,
  "requestType": "letter",
  "senderEmail": "sender@example.com",
  "status": "checkout_open",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:00.000Z"
}

The upload limit is 50 MB. Postcard images receive a temporary public URL because the print flow fetches the image later. Letter attachments and stamp images stay private.

Endpoint reference

This is the current customer-facing API surface.

POST /v1/compose-links
Send one recipient and an optional message. The response URL opens PieterPost with a secure draft token.
Request
{
  "externalId": "lead_123",
  "locale": "en",
  "message": "Hi Pieter,\nWould you like to come by on Sunday?",
  "metadata": {
    "source": "crm"
  },
  "recipient": {
    "city": "Springfield",
    "country": "United States",
    "name": "Pieter Post",
    "postalCode": "62704",
    "state": "IL",
    "streetAddress": "123 Main Street"
  }
}
Response
{
  "expiresAt": "2026-04-28T10:00:00.000Z",
  "id": "cl_123",
  "mode": "compose_link",
  "testMode": true,
  "url": "https://pieterpost.com/en/?draft=..."
}
Letter checkout
Send requestType: "letter" with a letters array.
Request
{
  "externalId": "invoice_123",
  "letters": [
    {
      "message": "Thanks for your order. Your receipt is enclosed below.",
      "recipient": {
        "city": "Springfield",
        "country": "United States",
        "name": "Pieter Post",
        "postalCode": "62704",
        "state": "IL",
        "streetAddress": "123 Main Street"
      }
    }
  ],
  "locale": "en",
  "metadata": {
    "customerId": "cus_123"
  },
  "requestType": "letter",
  "returnUrl": "https://example.com/pieterpost-return",
  "senderEmail": "sender@example.com"
}
Response
{
  "amountCents": 200,
  "billingMode": "hosted_checkout",
  "checkoutUrl": "https://pieterpost.com/v1/sandbox/checkout/ord_123",
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": "2026-04-22T10:00:00.000Z",
  "externalId": "invoice_123",
  "fulfilledAt": null,
  "metadata": {
    "customerId": "cus_123"
  },
  "orderId": "ord_123",
  "orderRef": "5b5b8d3f-7b83-4a6f-9fb8-0c1c3f9b8d8c",
  "paidAt": null,
  "paymentReference": null,
  "recipientCount": 1,
  "requestType": "letter",
  "senderEmail": "sender@example.com",
  "status": "checkout_open",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:00.000Z"
}
Bulk template letter checkout
For bulk template letters, send composeMode: "template" with a shared templateMessage, per-recipient customFields, and optional stampImageAssetId.
Request
{
  "composeMode": "template",
  "externalId": "spring_campaign_123",
  "letters": [
    {
      "recipient": {
        "city": "Springfield",
        "country": "United States",
        "name": "Pieter Post",
        "postalCode": "62704",
        "state": "IL",
        "streetAddress": "123 Main Street",
        "customFields": {
          "gift_code": "SPRING-10"
        }
      }
    },
    {
      "recipient": {
        "city": "Springfield",
        "country": "United States",
        "name": "Sam Example",
        "postalCode": "62704",
        "state": "IL",
        "streetAddress": "123 Main Street",
        "customFields": {
          "gift_code": "SPRING-20"
        }
      }
    }
  ],
  "locale": "en",
  "requestType": "letter",
  "returnUrl": "https://example.com/pieterpost-return",
  "senderEmail": "sender@example.com",
  "stampImageAssetId": "asset_stamp_123",
  "templateMessage": "Hi {{first_name}},\nYour spring code is {{gift_code}}.",
  "variableKeys": [
    "gift_code"
  ]
}
Response
{
  "amountCents": 460,
  "billingMode": "hosted_checkout",
  "checkoutUrl": "https://pieterpost.com/v1/sandbox/checkout/ord_457",
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": "2026-04-22T10:00:00.000Z",
  "externalId": "spring_campaign_123",
  "fulfilledAt": null,
  "metadata": {},
  "orderId": "ord_457",
  "orderRef": "ac42e951-6323-4055-b82f-cb2e8b2847bc",
  "paidAt": null,
  "paymentReference": null,
  "recipientCount": 2,
  "requestType": "letter",
  "senderEmail": "sender@example.com",
  "status": "checkout_open",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:00.000Z"
}
Postcard checkout
Send requestType: "postcard" with a postcard object. This example uses paymentMethod: "ideal".
Request
{
  "locale": "en",
  "paymentMethod": "ideal",
  "postcard": {
    "frontImageAssetId": "asset_123",
    "message": "A small card from PieterPost.",
    "recipient": {
      "city": "Springfield",
      "country": "United States",
      "name": "Pieter Post",
      "postalCode": "62704",
      "state": "IL",
      "streetAddress": "123 Main Street"
    }
  },
  "requestType": "postcard",
  "returnUrl": "https://example.com/pieterpost-return",
  "senderEmail": "sender@example.com"
}
Response
{
  "amountCents": 250,
  "billingMode": "hosted_checkout",
  "checkoutUrl": "https://pieterpost.com/v1/sandbox/checkout/ord_456",
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": "2026-04-22T10:00:00.000Z",
  "externalId": null,
  "fulfilledAt": null,
  "metadata": {},
  "orderId": "ord_456",
  "orderRef": "8cfa9ca3-11ec-43b5-b16a-7c0c48b8d124",
  "paidAt": null,
  "paymentReference": null,
  "recipientCount": 1,
  "requestType": "postcard",
  "senderEmail": "sender@example.com",
  "status": "checkout_open",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:00.000Z"
}
POST/v1/orders

Create direct orders

Create a direct-send order paid from wallet credits. Test keys simulate the flow; live keys send real mail when the wallet has enough balance.

Direct letter order
Use the same letter shape as hosted checkout, without returnUrl.
Request
{
  "letters": [
    {
      "message": "This direct-send letter is paid from wallet credits.",
      "recipient": {
        "city": "Springfield",
        "country": "United States",
        "name": "Pieter Post",
        "postalCode": "62704",
        "state": "IL",
        "streetAddress": "123 Main Street"
      }
    }
  ],
  "locale": "en",
  "metadata": {
    "campaign": "spring"
  },
  "requestType": "letter",
  "senderEmail": "sender@example.com"
}
Response
{
  "amountCents": 200,
  "billingMode": "credits",
  "checkoutUrl": null,
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": null,
  "externalId": null,
  "fulfilledAt": "2026-04-21T10:00:05.000Z",
  "metadata": {
    "campaign": "spring"
  },
  "orderId": "ord_789",
  "orderRef": "7d5118b8-7a91-4e7a-8b92-c72bd46c8432",
  "paidAt": "2026-04-21T10:00:02.000Z",
  "paymentReference": "wallet:ledger_123",
  "recipientCount": 1,
  "requestType": "letter",
  "senderEmail": "sender@example.com",
  "status": "fulfilled",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:05.000Z",
  "wallet": {
    "balanceCents": 2300,
    "currency": "eur",
    "mode": "test"
  }
}
Direct postcard order
Direct postcards use the same postcard object and are paid from credits.
Request
{
  "locale": "en",
  "postcard": {
    "frontImageAssetId": "asset_postcard_front_123",
    "message": "A credits-funded postcard from PieterPost.",
    "recipients": [
      {
        "city": "Springfield",
        "country": "United States",
        "name": "Pieter Post",
        "postalCode": "62704",
        "state": "IL",
        "streetAddress": "123 Main Street"
      }
    ]
  },
  "requestType": "postcard",
  "senderEmail": "sender@example.com"
}
Response
{
  "amountCents": 200,
  "billingMode": "credits",
  "checkoutUrl": null,
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": null,
  "externalId": null,
  "fulfilledAt": "2026-04-21T10:00:05.000Z",
  "metadata": {},
  "orderId": "ord_790",
  "orderRef": "8e25cad3-0f24-4a28-a55a-6ce879ec5d4a",
  "paidAt": "2026-04-21T10:00:02.000Z",
  "paymentReference": "wallet:ledger_124",
  "recipientCount": 1,
  "requestType": "postcard",
  "senderEmail": "sender@example.com",
  "status": "fulfilled",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:05.000Z",
  "wallet": {
    "balanceCents": 2300,
    "currency": "eur",
    "mode": "test"
  }
}
GET/v1/orders/:orderId

Track orders

Fetch an API order by id with the same API account that created it.

Get order status
Use order lookups for support, retries, and back-office reconciliation.
Request
curl https://pieterpost.com/v1/orders/ord_123 \
  -H "Authorization: Bearer pp_test_your_key_here"
Response
{
  "amountCents": 200,
  "billingMode": "hosted_checkout",
  "checkoutUrl": "https://pieterpost.com/v1/sandbox/checkout/ord_123",
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "error": null,
  "expiresAt": "2026-04-22T10:00:00.000Z",
  "externalId": "invoice_123",
  "fulfilledAt": null,
  "metadata": {
    "customerId": "cus_123"
  },
  "orderId": "ord_123",
  "orderRef": "5b5b8d3f-7b83-4a6f-9fb8-0c1c3f9b8d8c",
  "paidAt": null,
  "paymentReference": null,
  "recipientCount": 1,
  "requestType": "letter",
  "senderEmail": "sender@example.com",
  "status": "checkout_open",
  "testMode": true,
  "updatedAt": "2026-04-21T10:00:00.000Z"
}
GET/v1/wallet?currency=eur

Read wallet balance

Read API account capabilities and the current wallet balance.

Wallet capabilities
Use GET /v1/wallet?currency=eur before direct-send flows to check the current credit balance.
Request
curl https://pieterpost.com/v1/wallet?currency=eur \
  -H "Authorization: Bearer pp_test_your_key_here"
Response
{
  "accountId": "acct_123",
  "approvalStatus": "approved",
  "approvedForDirectSend": true,
  "creditsEnabled": true,
  "hostedApiEnabled": true,
  "liveEnabled": true,
  "wallet": {
    "balanceCents": 2500,
    "currency": "eur",
    "mode": "test",
    "updatedAt": "2026-04-21T10:00:00.000Z"
  }
}
POST/v1/credits/topups

Create credit top-ups

Create a wallet top-up. Test mode applies credits immediately; live mode returns a Stripe Checkout URL that you send the payer to.

Test top-up
Test mode creates and pays the top-up in one response.
Request
{
  "amountCents": 2500,
  "currency": "eur",
  "metadata": {
    "source": "billing-settings"
  },
  "returnUrl": "https://example.com/topups/return"
}
Response
{
  "amountCents": 2500,
  "checkoutUrl": null,
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "paidAt": "2026-04-21T10:00:01.000Z",
  "status": "paid",
  "testMode": true,
  "topupId": "topup_123",
  "updatedAt": "2026-04-21T10:00:01.000Z",
  "wallet": {
    "balanceCents": 2500,
    "currency": "eur",
    "mode": "test"
  }
}
Live top-up
Live mode returns a pending top-up with `checkoutUrl`. Open or redirect to that Stripe URL; the wallet is credited after payment succeeds.
Request
{
  "amountCents": 2500,
  "currency": "eur",
  "metadata": {
    "source": "billing-settings"
  },
  "returnUrl": "https://example.com/topups/return"
}
Response
{
  "amountCents": 2500,
  "checkoutUrl": "https://checkout.stripe.com/c/pay/...",
  "createdAt": "2026-04-21T10:00:00.000Z",
  "currency": "eur",
  "paidAt": null,
  "status": "pending",
  "testMode": false,
  "topupId": "topup_456",
  "updatedAt": "2026-04-21T10:00:00.000Z"
}
Payment method
Live top-ups default to paymentMethod: "auto". For EUR top-ups, auto offers both card and iDEAL in Stripe Checkout. Pass paymentMethod: "card" or paymentMethod: "ideal" only when you want to force one method.
Live response
With a pp_live_... key, the response is pending and includes checkoutUrl. Redirect the payer to that Stripe Checkout URL, or show it as a payment link.
Completion
Live payment completes asynchronously. After Stripe confirms payment, PieterPost marks the top-up paid and credits the live wallet.
Return URL
If you pass returnUrl, Checkout sends the payer back with topup=success or topup=cancelled. Use GET /v1/wallet after payment to confirm the credited balance.
POST/v1/uploads

Upload assets

Upload a temporary file with an API key before creating the order payload that references it.

Authentication
Use the same bearer-key authentication as other /v1 endpoints. Send multipart form-data instead of JSON.
Kinds
Use postcard-image for optional square 1:1 PNG/JPEG postcard fronts, letter-attachment for PDF, PNG, or JPEG letter attachments, and letter-stamp-image for PNG/JPEG custom letter stamps.

Orders and status

Order statuses are intentionally small so integrations can branch on a stable lifecycle.

Hosted checkout success
draft -> checkout_open -> paid -> queued -> fulfilled. The checkoutUrl is present while checkout is open.
Hosted checkout not paid
checkout_open can become cancelled when the customer cancels or expired after the checkout expiry window.
Direct send success
draft -> paid -> queued -> fulfilled. Wallet debit happens before fulfillment starts.
Failures
failed means validation, payment, wallet debit, provider handoff, or server configuration stopped the order.
draft
The order exists, but checkout or fulfillment has not started yet.
checkout_open
The hosted checkout URL is ready and waiting for payment.
paid
Payment or wallet debit succeeded.
queued
The order is queued for fulfillment.
fulfilled
The order has been handed to the mail fulfillment flow.
failed
The order could not be completed. Check the error field.
cancelled or expired
The hosted checkout was cancelled or was not paid before expiry.

Limits

Release limits keep the public API safe while leaving room for normal integration testing.

Minute request limit
Each API account is limited to 60 requests per minute across all keys. Individual keys and source IPs are also checked.
Daily order limit
Each API account can create up to 100 API orders per UTC day across test and live modes.
Batch size
Each letter or postcard order can include up to 25 recipients.

Errors

Errors return a short machine-readable code and a human-readable message.

Error response
{
  "code": "invalid_request",
  "error": "Hosted checkout links require a valid senderEmail."
}
Authentication
missing_api_key, invalid_api_key, invalid_api_key_mode, api_key_inactive, and account_inactive.
Validation
invalid_json and invalid_request cover malformed JSON, invalid recipients, missing messages, missing sender emails, and bad return URLs.
Access and limits
rate_limited, daily_order_cap_reached, and live_order_cap_reached.
Server configuration
misconfigured, db_error, and unknown_error mean the request reached PieterPost but the server could not complete it.