PostalForm Projects Developer documentation

PostalForm Projects API Reference

Send real postal mail from code: upload a PDF, quote it, create the order, and track it to the mailbox with signed webhooks. Free simulated test mode, prepaid credits for live mail, and Stripe Projects provisioning.

Start in 5 minutes Download OpenAPI YAML Download OpenAPI JSON Download SDK YAML Create workspace
01 · Overview

One mail API, two modes

Every PostalForm Projects workspace has isolated test and live environments. Test keys (pf_test_…) run the entire flow — uploads, quotes, orders, status timelines, and webhooks — against simulated fulfillment, free of charge. Live keys (pf_live_…) create real mail and spend prepaid credits only after the mailpiece is accepted for fulfillment.

Dashboard: https://projects.postalform.com
API:       https://projects.postalform.com/api/v1

Authenticate every API call with a bearer key. Fulfillment routing is automatic: you choose the mailpiece options (speed, color, proof mail, destination) and PostalForm selects an eligible production path.

Authorization: Bearer pf_test_...
Authorization: Bearer pf_live_...
CapabilityTest modeLive mode
FulfillmentSimulated end to endReal print-and-mail fulfillment
CostFree (simulated test credits)Prepaid credits, captured on acceptance
WebhooksDelivered and signed normallyDelivered and signed normally
TrackingSimulated status progressionCarrier tracking numbers where available
02 · Quick Start

Send your first letter in five minutes

Get a workspace

Via Stripe Projects

One command provisions a free workspace — no signup flow — and Stripe syncs the env vars into your secret store.

stripe projects add postalform/mail
Via direct signup

Create a workspace at projects.postalform.com/signup, then copy your key from the API Keys page.

POSTALFORM_TEST_API_KEY=pf_test_...
POSTALFORM_WORKSPACE_ID=pfws_...

Run the flow

  1. Create a PDF upload intent, PUT the PDF bytes to the returned URL, and mark the document complete.
  2. Create a letter quote with the print, mail class, country, and proof-mail options you want.
  3. Create the order with an Idempotency-Key header.
  4. Poll the order or listen for signed webhooks as it moves toward delivery.
export POSTALFORM_API_KEY="pf_test_..."
export POSTALFORM_API_BASE="https://projects.postalform.com/api/v1"

# 1. Upload a PDF
curl -s "$POSTALFORM_API_BASE/documents/upload-intent" \
  -H "Authorization: Bearer $POSTALFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content_type":"application/pdf","byte_size":123456,"page_count":1}'
# → returns document_id + upload_url

curl -s -X PUT "<upload_url>" \
  -H "Content-Type: application/pdf" \
  --data-binary @letter.pdf

curl -s -X POST "$POSTALFORM_API_BASE/documents/<document_id>/complete" \
  -H "Authorization: Bearer $POSTALFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"page_count":1}'

# 2. Quote it
curl -s "$POSTALFORM_API_BASE/letters/quotes" \
  -H "Authorization: Bearer $POSTALFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"document_id":"doc_..."}'
# → returns quote_id + price_cents

# 3. Send it
curl -s "$POSTALFORM_API_BASE/letters" \
  -H "Authorization: Bearer $POSTALFORM_API_KEY" \
  -H "Idempotency-Key: first-letter-2026-07-01" \
  -H "Content-Type: application/json" \
  -d '{"quote_id":"quote_...","recipient":{"name":"Ada Lovelace","line1":"1 Analytical Way","city":"San Francisco","state":"CA","postal_code":"94105"}}'

# 4. Watch it move
curl -s "$POSTALFORM_API_BASE/letters/<order_id>" \
  -H "Authorization: Bearer $POSTALFORM_API_KEY"

Prefer zero code first? The dashboard Send page runs this whole flow from a file picker. Use a pf_live_... key only when you intend to send real mail.

Typical test flow

POST /api/v1/documents/upload-intent
PUT  {upload_url}
POST /api/v1/documents/{document_id}/complete
POST /api/v1/letters/quotes
POST /api/v1/letters
GET  /api/v1/letters/{order_id}
GET  /api/v1/letters/{order_id}/document.pdf
03 · Documents

Document Uploads

Create an upload intent for a PDF, PUT the bytes to the returned upload URL, then mark the document complete. page_count is optional but recommended because it makes quotes deterministic before the file is inspected.

POST /api/v1/documents/upload-intent
{
  "content_type": "application/pdf",
  "byte_size": 123456,
  "page_count": 4
}

POST /api/v1/documents/{document_id}/complete
{
  "page_count": 4
}

Upload URLs expire — finish the PUT and the complete call promptly after creating the intent. A validated document can back multiple quotes.

04 · Letters

Letter Quotes

Quotes calculate the customer price and lock in the mail options for a later order. PostalForm automatically selects an eligible fulfillment path; API clients choose the mailpiece options, not the underlying production network.

POST /api/v1/letters/quotes
{
  "document_id": "doc_...",
  "mail_class": "usps_first_class",
  "color": false,
  "double_sided": true,
  "certified": false,
  "certified_return_receipt": false,
  "signature_required": false,
  "destination_country_code": "US",
  "origin_country_code": "US"
}

mail_class accepts standard/usps_first_class, priority/usps_priority, and express/usps_express. Priority and Express cannot be combined with certified or registered proof mail.

color controls black-and-white versus color printing. double_sided controls duplex printing when supported. Billing includes an inserted address page for letter mail.

Pricing

Pricing is separate from the one-off PostalForm.com workflow: standard letters start with a $1.80 base fee plus $0.20 per billable black-and-white page or $0.40 per billable color page. Proof-mail add-ons are $8.00 for U.S. Certified Mail, $32.00 for Canada Post Registered Mail, and $3.00 for Electronic Return Receipt where eligible. The quote is always the final price — estimate combinations on the pricing calculator.

Proof mail

Set certified=true for proof-mail services on eligible standard letters. U.S. destinations request USPS Certified Mail. Canadian destinations request Canada Post Registered Mail. Belgium, Switzerland, and France destinations request the available registered-mail option for that destination.

certified_return_receipt=true applies only to eligible U.S. Certified Mail. For Canada Post Registered Mail, the return-receipt flag is ignored because the registered service already includes the available delivery proof.

signature_required=true applies to eligible U.S. Priority and Express letters. Postcards do not support certified, registered, return-receipt, or signature-required options.

International mailing

Letter quotes accept destination_country_code and origin_country_code; both default to US. Supported first-party destination countries are US, CA, AT, BE, CH, DE, FR, GB, IN, LU, and NL.

Fast international mail is available only for destinations with a distinct fast-mail product. If a requested option is unavailable for the destination, the quote request returns a validation error instead of silently changing the requested service.

For international letters, include a sender address. Some destinations require a sender country that is compatible with the selected mailpiece options.

05 · Postcards

Postcards

Postcards use /api/v1/postcards/quotes and /api/v1/postcards. The uploaded PDF must already be a composed two-page postcard artwork file: page 1 is the front and page 2 is the back.

POST /api/v1/postcards/quotes
{
  "document_id": "doc_...",
  "postcard_size": "4x6",
  "destination_country_code": "US",
  "origin_country_code": "US"
}

postcard_size accepts 4x6, 6x9, or 11x6. Postcards are quoted as first-class, color, double-sided mail and do not support proof-mail add-ons.

06 · Orders

Create Orders

Create an order from a quote with an Idempotency-Key header. Reusing the same key with the same quote returns the original order with idempotent_replay=true, so network retries never create duplicate physical mail.

POST /api/v1/letters
Idempotency-Key: unique-order-key
{
  "quote_id": "quote_...",
  "recipient": { "name": "...", "line1": "...", "city": "...", "postal_code": "...", "countryCode": "US" },
  "sender": { "name": "...", "line1": "...", "city": "...", "postal_code": "...", "countryCode": "US" },
  "metadata": { "customer_id": "..." }
}

recipient is required. sender is strongly recommended for all live mail and required for some destinations and mailpiece options. metadata is stored on the order and returned in reads and webhooks.

Order responses

Order reads return the customer-facing mailpiece state, pricing, funds state, metadata, timeline, customer webhook events, and any tracking number that has been received.

GET /api/v1/letters/{order_id}

{
  "id": "ord_123",
  "object": "letter",
  "mailpiece_type": "letter",
  "status": "in_transit",
  "mode": "live",
  "price_cents": 220,
  "currency": "usd",
  "funds_status": "captured",
  "billing_rail": "prepaid_credits",
  "tracking_number": "9405500000000000000001",
  "tracking_status": "in_transit",
  "metadata": { "customer_id": "..." },
  "timeline": [],
  "webhook_events": []
}

Status lifecycle

Status values include queued, document_preparing, document_prepared, submitted, accepted, in_transit, delivered, returned, submission_pending, failed, and canceled.

submission_pending is a safety marker: the fulfillment job entered a window where PostalForm will not blindly retry, because a blind retry could create duplicate physical mail. If a later status follows it, it was transient. If it remains the current status, the order is being reconciled before anything else is mailed.

07 · Webhooks

Customer Webhooks

Use /api/v1/webhook-endpoints (or the dashboard) to register HTTPS endpoints. Customer webhook events are emitted for fulfillment status changes with postalform.{letter|postcard}.{status} event names: accepted, in_transit, delivered, returned, failed, and canceled.

Signing secrets are endpoint-scoped, start with whsec_, and are shown once when the endpoint is created or rotated — list calls never expose them. One endpoint can receive both test and live events.

PostalForm-Signature: t=1777592400,v1=<hmac_sha256_hex>

{
  "id": "evt_123",
  "type": "postalform.letter.in_transit",
  "data": {
    "object": {
      "id": "ord_123",
      "object": "letter",
      "status": "in_transit",
      "mode": "live",
      "price_cents": 220,
      "currency": "usd",
      "funds_status": "captured",
      "billing_rail": "prepaid_credits",
      "metadata": {}
    }
  },
  "mailpiece": {
    "status": "in_transit",
    "tracking_number": "9405500000000000000001",
    "tracking_status": "In Transit"
  }
}

Verify signatures

The signed payload is {timestamp}.{raw_json_body}, HMAC-SHA256 with your endpoint secret — the same shape as Stripe-style webhooks. Verify before trusting any delivery:

import { createHmac, timingSafeEqual } from 'node:crypto'

export function verifyPostalFormWebhook(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((part) => part.trim().split('=')))
  const timestamp = Number(parts.t)
  if (!Number.isFinite(timestamp) || Math.abs(Math.floor(Date.now() / 1000) - timestamp) > 300)
    return false

  const expected = createHmac('sha256', secret).update(timestamp + '.' + rawBody).digest('hex')
  const received = parts.v1 ?? ''
  return (
    received.length === expected.length &&
    timingSafeEqual(Buffer.from(received), Buffer.from(expected))
  )
}

Delivery and retries

Respond with a 2xx quickly. Failed deliveries retry up to six times with increasing backoff (about 1 minute, 5 minutes, 30 minutes, 2 hours, 12 hours, then 24 hours). Replay any event on demand with POST /api/v1/webhook-events/{event_id}/replay, and inspect every delivery attempt from the dashboard.

08 · Going live

Credits And Live Mail

Live mail spends prepaid credits: funds are reserved when an order is created and captured when fulfillment accepts the mailpiece. Top-ups are the only card charge that happens before mail is sent.

GET  /api/v1/credits/balance
POST /api/v1/credits/checkout-session
GET  /api/v1/credits/payment-methods
POST /api/v1/credits/payment-methods/setup-session
GET  /api/v1/credits/auto-refill
POST /api/v1/credits/auto-refill
GET  /api/v1/credits/ledger

Go-live checklist

  1. Finish your integration in test mode — the API surface is identical.
  2. Add prepaid credits from the dashboard Credits page or POST /api/v1/credits/checkout-session.
  3. Optionally save a payment method and configure auto-refill so live sends never fail on balance.
  4. Rotate a live key from the dashboard or POST /api/v1/api-keys/rotate with {"mode":"live"}.
  5. Send one live letter to yourself and watch the webhook events arrive.

Live keys stay disabled until the workspace is live-enabled through billing setup, so a leaked test integration can never mail anything real.

09 · Errors & limits

Errors And Rate Limits

Errors return a machine-readable code, with a 4xx status for request problems and 5xx for PostalForm-side failures.

{ "error": "idempotency_key_required" }
CodeStatusMeaning
unauthorized401Missing or invalid bearer key.
forbidden403The key cannot act on this resource (for example, live action on a test-only workspace).
not_found404Unknown route or a resource that does not belong to this workspace.
idempotency_key_required400Order creation without an Idempotency-Key header.
rate_limited429Too many requests; the response includes retry_after_seconds.

Each API key is limited to 120 requests per 60 seconds. Batch large jobs client-side and honor retry_after_seconds on 429 responses. Validation failures (an ineligible option combination, an expired quote, a malformed address) return a descriptive error code instead of silently changing the request.

10 · SDKs & schema

Generated Schema And SDKs

The hosted OpenAPI YAML is the customer API source of truth for SDK generation. It documents request bodies, response properties, enums, authentication, and webhook payloads.

GET /openapi.json
GET /openapi.public.json
GET /openapi.yaml
GET /openapi.public.yaml

Generated TypeScript (@postalform/projects-sdk) and Python (postalform-projects-sdk) clients are built from this schema. Until they are published to npm and PyPI, generate your own client from /openapi.public.yaml with any OpenAPI generator, or call the API directly — every endpoint works with plain HTTP.

Building with agents? GET /llm-context.txt serves a compact machine-readable summary of provisioning and this API.

Endpoint Reference

This table is generated from the public OpenAPI schema served by /openapi.json and /openapi.yaml.

MethodPathPurposeRequestPrimary response
POST/api/v1/documents/upload-intentCreate a PDF upload intent.objectUploadIntent
POST/api/v1/documents/{document_id}/completeMark an uploaded document complete after the object is present.objectobject
POST/api/v1/letters/quotesQuote a mailpiece.CreateQuoteRequestQuote
POST/api/v1/lettersCreate a test or live mail order from a quote.CreateLetterRequestLetter
GET/api/v1/letters/{order_id}Retrieve a mail order, timeline, tracking fields, and customer webhook events.NoneLetter + object
GET/api/v1/letters/{order_id}/document.pdfPreview or download the PDF for a letter order.NonePDF bytes
POST/api/v1/postcards/quotesQuote a postcard mailpiece.CreatePostcardQuoteRequestQuote
POST/api/v1/postcardsCreate a test or live postcard order from a postcard quote.CreateLetterRequestLetter
GET/api/v1/postcards/{order_id}Retrieve a postcard order, timeline, tracking fields, and customer webhook events.NoneLetter + object
GET/api/v1/postcards/{order_id}/document.pdfPreview or download the PDF for a postcard order.NonePDF bytes
GET/api/v1/webhook-endpointsList customer webhook endpoints for the workspace.NoneWebhookEndpoint[]
POST/api/v1/webhook-endpointsConfigure a customer webhook endpoint for status events.objectWebhookEndpointSecretResponse
DELETE/api/v1/webhook-endpoints/{endpoint_id}Disable a customer webhook endpoint.NoneWebhookEndpoint
POST/api/v1/webhook-endpoints/{endpoint_id}/rotate-secretRotate a customer webhook endpoint signing secret.NoneWebhookEndpointSecretResponse
GET/api/v1/webhook-eventsList customer webhook events for the workspace.NoneWebhookEvent[]
POST/api/v1/webhook-events/{event_id}/replayQueue a customer webhook event for replay.NoneReplay queued.
GET/api/v1/credits/balanceGet prepaid credit balance.NoneCreditBalance
GET/api/v1/credits/payment-methodsList saved live billing payment methods.NoneBillingPaymentMethod[]
POST/api/v1/credits/payment-methods/setup-sessionCreate a Checkout setup session for live credit auto-refill.NonePaymentMethodSetupSession
GET/api/v1/credits/auto-refillGet credit auto-refill threshold policies.NoneCreditAutoRefillPolicies
POST/api/v1/credits/auto-refillConfigure the credit auto-refill threshold for the authenticated key mode.CreditAutoRefillRequestobject
GET/api/v1/credits/ledgerList prepaid credit ledger entries.NoneCreditLedgerEntry[]
GET/api/v1/api-keysList API key prefixes and status.NoneApiKey[]
POST/api/v1/api-keys/rotateRotate a test or live API key.objectApiKeyRotation
POST/api/v1/credits/checkout-sessionCreate a prepaid credit top-up checkout session.objectCreditCheckoutSession

Configuration Reference

These request-body tables come from the same OpenAPI schema used for SDK generation.

Letter quote configuration

Letter quote request. API clients choose mailpiece options; PostalForm handles fulfillment automatically.

PropertyType / valuesRequiredDefaultMeaning
document_idstringYesNoneUploaded PDF to quote.
page_countinteger; min 1NoNoneOptional explicit PDF page count. Used for deterministic pricing when present.
mail_classstringNousps_first_classStandard/USPS First Class by default. Accepts standard/usps_first_class, priority/usps_priority, and express/usps_express. Priority/Express cannot be combined with certified or registered proof mail.
colorbooleanNofalseWhether the letter is printed in color instead of black and white.
double_sidedbooleanNotrueWhether eligible pages are printed duplex.
certifiedbooleanNofalseProof-mail add-on for letters only. Eligible U.S. standard letters request USPS Certified Mail. Eligible Canada standard letters request Canada Post Registered Mail. Eligible Belgium, Switzerland, and France standard letters request the available registered-mail option for that destination.
certified_return_receiptbooleanNofalseElectronic return receipt for eligible U.S. Certified Mail letters.
signature_requiredbooleanNofalseSignature confirmation for eligible U.S. Priority and Express letters.
destination_country_codestring; min length 2, max length 2NoUSISO 3166-1 alpha-2 destination country code used for quote validation and pricing. Defaults to US. First-party Projects destinations are US, CA, AT, BE, CH, DE, FR, GB, IN, LU, and NL.
origin_country_codestring; min length 2, max length 2NoUSISO 3166-1 alpha-2 sender/origin country code used for quote validation and pricing. Defaults to US.

Postcard quote configuration

Postcard quote request. Postcards force first-class color double-sided mail and cannot use certified or registered mail.

PropertyType / valuesRequiredDefaultMeaning
document_idstringYesNoneSee the schema type and endpoint context.
page_countinteger; min 1NoNoneOptional explicit PDF page count. Postcard source PDFs should be fully composed.
postcard_size4x6, 6x9, 11x6YesNoneSee the schema type and endpoint context.
destination_country_codestring; min length 2, max length 2NoUSISO 3166-1 alpha-2 destination country code used for quote validation and pricing. Defaults to US.
origin_country_codestring; min length 2, max length 2NoUSISO 3166-1 alpha-2 sender/origin country code used for quote validation and pricing. Defaults to US.

Create-order body

Create an order from a quote. Requires Idempotency-Key header. Sender is strongly recommended for all live mail and required for some destinations and mailpiece options.

PropertyType / valuesRequiredDefaultMeaning
quote_idstringYesNoneQuote returned by the quote endpoint.
recipientMailingAddressYesNoneDestination mailing address.
senderMailingAddressNoNoneReturn/sender address. Strongly recommended for all live mail.
metadataobject mapNoNoneOptional caller metadata stored on the order and surfaced in reads/webhook payloads.

Address object

Flexible mailing address object. Pass countryCode, country_code, country, address_country, or addressCountry for international destinations; country defaults to US when omitted.

PropertyType / valuesRequiredDefaultMeaning
namestringNoNoneRecipient or sender name.
companystringNoNoneOptional organization name.
line1stringNoNoneAlso accepts street1, address_line1, or addressLine1.
line2stringNoNoneAlso accepts street2, address_line2, or addressLine2.
citystringNoNoneAlso accepts address_city or addressCity.
statestringNoNoneAlso accepts province, provinceOrState, address_state, or addressState.
postal_codestringNoNoneAlso accepts postalCode, postalOrZip, zip, address_zip, or addressZip.
countryCodestring; min length 2, max length 2NoUSISO 3166-1 alpha-2 country code. Defaults to US when omitted.

Response Fields

These response tables explain the IDs, prices, state fields, timeline entries, and tracking fields returned by order and credit reads.

Upload intent response

PropertyType / valuesRequiredDefaultMeaning
document_idstringNoNoneStable document identifier used in quote requests.
upload_urlstring (uri)NoNoneTemporary URL that accepts the PDF bytes.
upload_methodPUTNoNoneHTTP method to use when uploading to the temporary URL.
expires_atstring (date-time)NoNoneWhen the temporary upload URL stops accepting uploads.

Quote response

PropertyType / valuesRequiredDefaultMeaning
quote_idstringNoNoneShort-lived quote token used to create the order.
mailpiece_typeletter, postcardNoNoneWhether the quote is for a letter or postcard.
postcard_size4x6, 6x9, 11x6, nullNoNonePostcard size for postcard quotes; null for letters.
price_centsintegerNoNoneCustomer price in cents for the quoted mailpiece.
currencyusdNoNoneQuote currency.
pricing_versionstringNoNonePricing table version used for this quote.
certified_return_receiptbooleanNoNoneWhether electronic return receipt was accepted for the quote.
signature_requiredbooleanNoNoneWhether signature confirmation was accepted for the quote.
expires_atstring (date-time)NoNoneWhen the quote can no longer be used to create an order.

Order response

PropertyType / valuesRequiredDefaultMeaning
idstringNoNonePostalForm order ID.
objectletter, postcardNoNoneObject type returned by the API.
mailpiece_typeletter, postcardNoNoneMailpiece family for the order.
postcard_size4x6, 6x9, 11x6, nullNoNonePostcard size for postcard orders; null for letters.
statusMailOrderStatusNoNoneCustomer-facing order state.
modeModeNoNoneWhether the order was created with a test or live key.
price_centsintegerNoNoneFinal customer price reserved or captured for the order.
currencystringNoNoneOrder currency.
funds_statusstringNoNoneBilling state for the order funds.
billing_railstringNoNoneBilling source used for the order.
tracking_numberstring | nullNoNoneCarrier tracking number when available.
tracking_statusstring | nullNoNoneNormalized tracking status when available.
metadataobject mapNoNoneCaller-defined metadata copied from the create-order request.
created_atstring (date-time)NoNoneOrder creation timestamp.
updated_atstring (date-time)NoNoneMost recent order update timestamp.
idempotent_replaybooleanNoNoneTrue when the response came from a repeated idempotency key.

Order timeline event

Customer-facing order timeline event. Internal fulfillment identifiers are not exposed.

PropertyType / valuesRequiredDefaultMeaning
idstringNoNoneTimeline event ID.
event_typestringNoNoneCustomer-facing event type.
status_beforestringNoNoneOrder status before the event, when known.
status_afterstringNoNoneOrder status after the event, when known.
sourcestringNoNonePublic source category for the status update.
created_atstring (date-time)NoNoneTimeline event timestamp.

Credit balance response

PropertyType / valuesRequiredDefaultMeaning
modetest, liveNoNoneKey mode for the returned balance.
billing_railmock_test_credits, prepaid_credits, future_projects_spt, future_metronome_streaming, future_mpp_tempo, manual_invoiceNoNoneBilling source currently backing credits.
available_centsintegerNoNoneCredit cents available for new live orders.
reserved_centsintegerNoNoneCredit cents reserved for created orders that are not fully settled.
currencystringNoNoneBalance currency.
statusstringNoNoneCredit account status.
testCreditBalanceSnapshotNoNoneTest-mode balance snapshot.
liveCreditBalanceSnapshotNoNoneLive-mode balance snapshot.
auto_refillCreditAutoRefillPoliciesNoNoneConfigured auto-refill policies.

Webhook And Key Fields

Webhook payloads use customer-facing order states. Store webhook signing secrets when they are created or rotated because they are returned once.

Webhook endpoint

PropertyType / valuesRequiredDefaultMeaning
idstringNoNoneWebhook endpoint ID.
urlstring (uri); pattern ^https://NoNoneHTTPS URL that receives signed status webhooks.
statusstringNoNoneEndpoint lifecycle status.
created_atstring (date-time)NoNoneEndpoint creation timestamp.

Webhook endpoint secret response

PropertyType / valuesRequiredDefaultMeaning
idstringNoNoneSee the schema type and endpoint context.
urlstring (uri); pattern ^https://NoNoneSee the schema type and endpoint context.
statusstringNoNoneSee the schema type and endpoint context.
created_atstring (date-time)NoNoneSee the schema type and endpoint context.
signing_secretstringNoNoneReturned only when the endpoint is created or its signing secret is rotated.

Customer webhook payload

JSON body POSTed to customer webhook endpoints.

PropertyType / valuesRequiredDefaultMeaning
idstringNoNoneWebhook event ID.
typeCustomerWebhookEventTypeNoNonePostalForm event name.
dataobjectNoNoneWebhook data wrapper. data.object is the current order object.
mailpieceobjectNoNoneTracking and delivery-state snapshot for the physical mailpiece.

Auto-refill configuration

PropertyType / valuesRequiredDefaultMeaning
modeModeNoNoneOptional key mode override. Defaults to the authenticated key mode.
enabledbooleanNotrueWhether automatic refill is enabled.
threshold_centsinteger; min 100, max 100000YesNoneTrigger a refill when available credits fall below this amount.
refill_amount_centsinteger; min 100, max 100000YesNoneCredit amount to purchase when the threshold is crossed.

API key rotation response

PropertyType / valuesRequiredDefaultMeaning
modeModeNoNoneKey mode that was rotated.
api_keystringNoNoneFull API key secret. Returned once.
api_key_prefixstringNoNoneNon-secret key prefix shown later in dashboards and list responses.

Order status values

Order timeline status. `submission_pending` means the fulfillment submit job entered a physical-mail safety window where PostalForm cannot blindly retry without risking duplicate mail; it requires reconciliation if it remains the current order status.

ValueMeaning
queuedOrder accepted and waiting for document preparation.
document_preparingPostalForm is preparing the print-ready PDF.
document_preparedThe print-ready PDF is ready for submission.
submittedThe mailpiece has been submitted for production.
acceptedThe mailpiece was accepted for production or mailing.
in_transitThe mailpiece is moving through the mail stream.
deliveredDelivery was reported.
returnedThe mailpiece was returned.
submission_pendingSubmission entered a safety window and needs reconciliation before blind retry.
failedThe order failed and needs review or retry.
canceledThe order was canceled.

Customer webhook event types

Customer webhook event names emitted for fulfillment status changes. Replace `letter` with `postcard` for postcard mailpieces.

ValueMeaning
postalform.letter.acceptedLetter accepted for production or mailing after the order leaves PostalForm's preparation queue.
postalform.letter.in_transitLetter entered the mail stream. The payload may include or update `mailpiece.tracking_number` when tracking is available.
postalform.letter.deliveredLetter reported delivered by the carrier or delivery network.
postalform.letter.returnedLetter returned or otherwise marked undeliverable.
postalform.letter.failedLetter could not be produced or mailed. Inspect the order timeline and retry with a corrected request when appropriate.
postalform.letter.canceledLetter canceled before delivery completion.
postalform.postcard.acceptedPostcard accepted for production or mailing after the order leaves PostalForm's preparation queue.
postalform.postcard.in_transitPostcard entered the mail stream. The payload may include or update `mailpiece.tracking_number` when tracking is available.
postalform.postcard.deliveredPostcard reported delivered by the carrier or delivery network.
postalform.postcard.returnedPostcard returned or otherwise marked undeliverable.
postalform.postcard.failedPostcard could not be produced or mailed. Inspect the order timeline and retry with a corrected request when appropriate.
postalform.postcard.canceledPostcard canceled before delivery completion.

API key modes

ValueMeaning
testTest mode.
liveReal mail and live billing.