postalform-agent-mailing
v1.0.0Send real postal mail through PostalForm using machine payments: prepare/validate print-and-mail payloads, submit `POST /api/machine/orders`, settle x402 payment with any compatible wallet client (purl or custom), and poll fulfillment through completion. Use when an agent must autonomously mail a re...
Installation
PostalForm Machine Order
Use this workflow when an agent needs to send real postal mail (a physical print-and-mail order) reliably on the first attempt.
Workflow
1. Collect inputs and choose address strategy
Require these inputs:
- buyer_name, buyer_email
- sender_name, recipient_name
- PDF source (upload_token, { download_url, file_id }, data URL, or allowed https URL)
- Mailing options (double_sided, color, mail_class, certified)
For each party (sender, recipient), choose exactly one strategy:
- Manual address: *_address_type: "Manual" + *_address_manual
- Loqate address: *_address_type: "Address" + *_address_id + *_address_text
Do not mix manual + Loqate for the same party.
2. Build payload with strict idempotency
Generate a UUID request_id once and keep payload bytes stable across retries.
Set buyer_email every time (required for Stripe receipt routing).
For manual addresses:
- Include line1, city, state, zip
- Include line2 only when it has a non-empty string value
- Omit optional fields instead of sending null
Use the tested template in references/payload_templates.md.
3. Preflight with validate endpoint (recommended)
Call:
- POST https://postalform.com/api/machine/orders/validate
If response is 200, confirm:
- quote.page_count matches expected page count
- quote.price_usd and options are acceptable
If response is 422, fix payload before paying.
4. Create order and settle x402 payment
Endpoint:
- POST https://postalform.com/api/machine/orders
Flow:
1. Send order payload without payment header.
2. Receive 402 with PAYMENT-REQUIRED.
3. Create payment using your wallet stack on the requested network.
4. Retry the exact same request body with PAYMENT-SIGNATURE.
5. Expect 202 and settlement metadata.
Payment client options:
- purl CLI (fastest path when available)
- Any x402-compliant client (@x402/core, @x402/evm, or equivalent custom signer flow)
If using purl:
- Prefer wallet alias/keystore over raw private keys
- Use --password from secure runtime input
- Set a protective --max-amount
- Match --network to PAYMENT-REQUIRED network
5. Poll order status until post-payment completion
Call:
- GET https://postalform.com/api/machine/orders/:request_id
Treat this as complete when:
- is_paid = true
- current_step has advanced through processing (commonly to email_sent)
- order_complete_url is a real URL
Handle transitional state:
- status: "settled_pending_webhook" can persist briefly after on-chain settlement
- Continue polling with backoff; do not resubmit a changed payload under the same request_id
First-Pass Reliability Rules
Required invariants
- Keep
request_idconstant for retries of the same logical order. - Keep payload unchanged when retrying after
402. - Send
buyer_emailalways. - Match payment network to
PAYMENT-REQUIRED; do not hardcode network. - Use only valid US addresses and one address strategy per party.
- Verify page count and quoted price before payment.
Observed failure modes and prevention
422 invalid_typeon manualline2:- Cause: empty optional fields normalized as
null. - Prevent: omit
line2unless non-empty string. - Delay after successful payment (
settled_pending_webhook): - Cause: settlement acknowledged before webhook reconciliation completes.
- Prevent: poll for up to several minutes with retry/backoff.
- Occasional non-JSON/transient server response during polling:
- Cause: temporary upstream/render errors.
- Prevent: parse defensively; if response is non-JSON or
5xx, retry without changingrequest_idor payload. 409 request_id_mismatch:- Cause: same
request_idreused with modified payload. - Prevent: generate a new UUID for changed orders; reuse ID only for exact retries.
Security Rules
- Avoid printing wallet passwords, private keys, or sensitive env vars in logs.
- Prefer encrypted keystore wallets over raw private keys when tooling supports both.
- Use a payment cap (
max amount) to avoid accidental overpayment.
Operator Output
Return these values after execution:
- request_id / order_id
- price/quote summary (including page_count)
- payment settlement details (network, pay_to, settlement_tx, settled_at)
- latest status (is_paid, current_step)
- order_complete_url when available
Reference examples and command snippets:
- references/payload_templates.md