# Prepare a letter or fax from JavaScript or Python Download [JavaScript (Node.js 22+)](https://postedly.com/examples/postedly.mjs) or [Python (3.10+)](https://postedly.com/examples/postedly.py). Both use only their standard runtimes. They check availability and account identity, upload the exact local file bytes with a SHA-256 checksum, save an immutable quote, obtain a private browser review link, and read recorded order status. The [public examples repository](https://github.com/johnarndt/postedly-examples) includes both implementations and runnable offline tests. The examples are covered by offline tests with simulated HTTP responses. Those tests do **not** establish a live payment, provider submission or delivery. No payment API is called by either example. The sender decides whether to approve and pay in the browser. ## Connect once Use an existing OAuth connection with `quotes:write checkout:write orders:read`. Follow the [registration, browser consent, PKCE and token guide](https://postedly.com/agent-guide.md#register-and-obtain-browser-consent) to obtain a token; no permanent API key or client secret is issued. The sender's email must match the verified connected account. Do not create a guest session to replace the connection. Have your application or secure credential manager inject the current access token into `POSTEDLY_ACCESS_TOKEN`. Do not paste it into source, a command line, chat, or a committed `.env` file. Token refresh belongs to the owning OAuth client: serialize refresh and replace both returned credentials atomically. These examples do not automatically refresh or replay requests. ## Prepare your own document and recipient Create a private `request.json` with real details you are authorized to use. The following is a **shape example only**: replace the names, email and addresses before requesting a quote. The email must be your verified account email. Keep this file outside a public repository. ```json { "service": "letter", "sender": { "name": "YOUR SENDER NAME", "email": "your-verified-email@example.com", "address1": "YOUR RETURN ADDRESS", "city": "YOUR CITY", "state": "CA", "postalCode": "YOUR ZIP CODE", "country": "US" }, "recipient": { "name": "YOUR RECIPIENT NAME", "address1": "YOUR RECIPIENT ADDRESS", "city": "RECIPIENT CITY", "state": "CA", "postalCode": "RECIPIENT ZIP CODE", "country": "US" }, "options": {} } ``` These examples support `fax`, `letter`, `certified`, and `postcard`. For fax, set `service` to `fax` and give the recipient `name`, `fax` (a valid US `+1` number) and `country: "US"`. Keep the sender's verified email and US billing location. For certified mail and postcard options, consult the [API contract](https://postedly.com/openapi.json) and [current service requirements](https://postedly.com/api/catalog); use the supported mailing class and exact postcard layout. Use a finished, unencrypted PDF for letters and faxes. Files must contain 1 byte through 10 MiB; service and product page/layout limits can be lower. `document.pdf` in these commands means the actual local file, not a URL or text extracted from it. Run **one** implementation: ```sh node postedly.mjs prepare request.json document.pdf private-review.json ``` ```sh python3 postedly.py prepare request.json document.pdf private-review.json ``` Each command refuses to overwrite an existing output. It saves private checkpoints after upload and quote creation so a subsequent failure does not erase the known IDs. On systems honoring POSIX file modes, only the owner can read the result (mode `0600`). Protect it with equivalent permissions on other platforms. Never commit the request, result, token, document or review URL. ## Review, approval and payment Open `private-review.json` locally. `quote` contains the final proof, recipient, sender, line items, total in integer cents and `expiresAt`. The quote is normally valid for 20 minutes. The proof may include a fax cover or postal layout and may differ from the uploaded source. `quote.currency` is `USD`. Open `review.url` privately in your browser. This is the result of the MCP `postedly_checkout` tool. The review page shows the exact proof/item, recipient, total and terms. Only the sender's explicit approval there proceeds to hosted Stripe Checkout. Generating this link creates no order, approval, payment or send. Do not paste private links into public logs or tickets. The REST `POST /api/checkout` endpoint has different semantics: it records document/terms approval and creates or reuses an order. These examples deliberately use the browser approval workflow and never set approval booleans for the sender. OAuth consent is not order approval. ## Read the existing order After browser checkout, retain the order ID from its Postedly status page. Run a separate read into a fresh private output: ```sh node postedly.mjs status private-status.json YOUR_EXISTING_ORDER_ID ``` ```sh python3 postedly.py status private-status.json YOUR_EXISTING_ORDER_ID ``` Omit the order ID to list up to 100 recent orders for the same owner. An order response uses lowercase `usd`, separate `paymentStatus` and `fulfillmentStatus`, a timeline, and optional provider evidence. `authorized` means a payment hold; `paid` does not mean mailed or delivered. Fax `delivered` means the approved pages reached the receiving machine, not that a person read them. Postal delivery information depends on provider milestones. ## Recover without a duplicate On a timeout, preserve the partial output and read existing orders/status. Do not rerun `prepare`, invent a replacement order, or blindly retry checkout/payment. A failed order-list read does not mean there are no orders. Use the retained document/quote ID to resume only the known unfinished step when its outcome is understood; both examples expose methods for applications to do so. If a quote expires, review the current order list and obtain a new quote only when there is no pending send/payment to resolve. For HTTP 401, let the owning OAuth client coordinate one normal refresh. Retry a read once after credential recovery; do not automatically replay a mutation. For HTTP 403, verify the granted scopes and sender identity. Follow the [recovery guide](https://postedly.com/agent-guide.md#recover-without-duplicating-a-send) for other errors. Both examples reject redirects so an authorization header cannot be forwarded to another host.