# POST Request > Mailbox-access verification delivered by the United States Postal Service. > You send a mailing address; we print and mail a card carrying a single-use > QR code; the card is completed; a signed webhook closes the loop. Standard > delivery is scheduled for 5–7 business days. Every attempt needs an address, > postage, and time. This is the machine-readable edition of the API reference. The human version lives at https://postrequest.dev/docs. Every endpoint is under https://postrequest.dev/api/v1, speaks JSON, and returns errors that say what to do next. ## Authentication Pass your API key as a Bearer token. Keys come in two modes — `pr_test_…` and `pr_live_…` — and the key you use decides which mode the verification is created in. There is no mode parameter; there is only the key. ``` curl https://postrequest.dev/api/v1/verifications \ -H "Authorization: Bearer pr_live_..." ``` ## Test mode Test mode is the whole product with the clock compressed: mail "prints" in ~10 seconds, "delivers" in ~60, costs nothing, and never touches your credit balance. The QR is shown in the dashboard so you can scan it with your own phone and be your own end user — same verification page, same webhook, same green stamp. Everything below behaves identically in both modes except the clock and the ledger. Start in test mode. ## POST /api/v1/verifications — create a verification Validates the address, debits your balance, prints a card, and mails it. Validation runs BEFORE the debit — a bad address is a 422, not a $1.25 lesson. No recipient name is accepted anywhere: mailpieces are addressed to CURRENT RESIDENT, by design. Body fields: | Field | Type | Notes | |---|---|---| | address | object | line1, line2?, city, state, zip. Required. | | class | string | `bulk` ($0.75) · `standard` ($1.25, default) · `certified` ($3.00) | | binding | string | `none` (default) or `email_otp` | | email | string | Required iff binding=email_otp. Stored encrypted, purged on schedule. | | external_id | string | Your reference. The dashboard manifest is searchable by it. | | return_url | string | https only. Shown to the user after the green stamp. | | metadata | object | Yours, returned verbatim. | ```json // 201 Created { "id": "01JZ3NVJ4M8Q...", "class": "standard", "status": "created", "price_cents": 125, "balance_cents": 9875, "expires_at": "2026-08-18T00:00:00Z", "signals": { "address_reuse_count": 0, "cluster_count": 0, "po_box": false, "cmra": null } } // 402 — insufficient credits. Nothing printed. See "Recommended patterns". { "error": "insufficient_credits", "balance_cents": 50, "required_cents": 125 } // 422 — the address failed validation. Nothing debited. { "error": "invalid_address", "field": "address.zip", "detail": "..." } // 429 — this address hit the global limit: 3 mailpieces per 7 days, // across ALL senders. Harassment protection; not negotiable. { "error": "address_rate_limited" } ``` ## GET /api/v1/verifications/:id — retrieve Returns the full record, the signals block, and the events timeline — CREATED → PRINTED → IN TRANSIT → DELIVERED → VERIFIED, with timestamps. It is USPS tracking, because that is what it is. ## GET /api/v1/verifications?status=&external_id=&limit= — list The manifest listing. Filter by status or your external_id. ## POST /api/v1/verifications/:id/resend — resend Voids the old QR token, prints a new card, mails it, and debits full price — new mailpiece, new postage. Returns 409 if the verification already succeeded or its address has been purged. ## DELETE /api/v1/verifications/:id/pii — erase PII Erasure passthrough for your users' deletion requests: the plaintext address and any bound email are destroyed immediately instead of at terminal + 90 days. Keyed fingerprints, the events timeline, and a redacted display form are retained. ## GET /api/v1/credits — balance & ledger Balance and the append-only ledger. Credits are dollars, held in cents. They never expire. ## POST /api/v1/credits/checkout — top up Returns a Stripe Checkout URL for a top-up. `amount_cents` minimum is 2500 — we sell books of stamps, not single stamps. Your card is saved in the same Checkout for auto-recharge. ## Webhooks Four events: `verification.delivered` / `.verified` / `.expired` / `.undeliverable`. Delivered is your cue to nudge your user ("your card arrived, go check the mail") and it is the cheapest conversion lever in the whole loop. Five delivery attempts with exponential backoff; every attempt is logged in the dashboard and can be redelivered. Payloads carry the keyed address fingerprint, never the address. ```json { "type": "verification.verified", "id": "01JZ3NVJ4M8Q...", "external_id": "user_8123", "class": "standard", "mode": "live", "status": "verified", "delivered_at": "2026-07-16T19:41:00Z", "verified_at": "2026-07-17T15:02:11Z", "binding": "email_otp", "address_fingerprint": "fp_9c44..." } ``` ## Verifying webhook signatures Every delivery is signed in the `X-Post-Request-Signature` header using the Stripe scheme: `t=timestamp,v1=hex`, where v1 is HMAC-SHA256 of `${timestamp}.${body}` under your signing secret. Compute the HMAC over the RAW request body, and reject if the timestamp is more than 300s old. ```js import { createHmac, timingSafeEqual } from "node:crypto"; function verify(header, body, secret) { const { t, v1 } = Object.fromEntries( header.split(",").map((p) => p.split("=")) ); if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; const expected = createHmac("sha256", secret) .update(`${t}.${body}`) .digest("hex"); const expectedBytes = Buffer.from(expected, "hex"); const receivedBytes = Buffer.from(v1, "hex"); return expectedBytes.length === receivedBytes.length && timingSafeEqual(expectedBytes, receivedBytes); } ``` ## What verification attests - `binding: none` — a party with physical access to mail at this address completed the loop. - `binding: email_otp` — the party controlling this email address also completed a credential delivered to this address. A stolen or photographed mailpiece cannot complete the loop without the email code, and the code cannot complete it without the mailpiece. Neither mode attests legal identity or one-human uniqueness. Abuse resistance comes from address economics, reuse counts, building-level clusters, PO-box detection, and customer policy. ## What we store The encrypted mailing address remains on the verification through terminal state + 90 days. It then moves to a separate encrypted vault without customer or verification IDs. Redacted address text and keyed fingerprints remain on the verification. Do not place personal data in `external_id` or `metadata`; those fields remain on the verification. | Data | Retention | |---|---| | Mailing address ciphertext | Moves to the delinked vault at terminal + 90 days. | | Delinked address vault | Encrypted. Retained 18 months with week-level dates. | | Redacted address + keyed fingerprint | Retained with the verification. | | Recipient name | Never collected. | | Email ciphertext, if bound | Removed at terminal + 90 days; hash + mask remain. | | External ID + metadata | Customer supplied. Retained with the verification. | | QR token | Hash only. Plaintext never stored. | | Scan signals | Keyed IP hash + ZIP3 bucket. Retained with the verification. | ## Recommended patterns - **The 402.** Treat verification creation as queueable: on 402, enqueue, alert yourself, retry after top-up. Your user was going to wait five days anyway; thirty minutes of queue delay is invisible. - **User-pays.** For human-only communities, pass the fee through. Each additional account now needs another deliverable-address allocation and a card payment. Nothing in the API changes — charge your user, then call us. - **The delivered nudge.** On `verification.delivered`, email your user. Cards expire, so use the event to prompt them to check the mail while the card is current. ## Rates (effective July 2026) | Class | Detail | Price | |---|---|---| | Bulk | postcard · batched weekly · 2–3 weeks | $0.75 | | Standard | postcard · mailed next day · 5–7 business days | $1.25 | | Certified | sealed letter · mailed next day | $3.00 | Pricing is per mailpiece, not per success. Effective cost per verified user is the mailpiece rate divided by your observed completion rate. We do not yet publish a production completion benchmark; measure your own cohort. Postage is non-refundable, with one exception: undeliverable Certified mail refunds 50%. Expired cards refund nothing — delivery happened; the recipient did not act. Credits never expire.