How it works
1
Upload the artwork
POST /assets with the file as the raw body. A letter is one PDF. A postcard is a front and a back.2
Preview
POST /preview with the two addresses and the artwork. OneShot checks both addresses, counts the pages, and starts rendering a proof. Nothing is charged.3
Wait for the quote
Poll
GET /quotes/{id} until status is ready. The quote then carries the proof and total_usdc. A quote expires 30 minutes after it is created.4
Approve
A person looks at the proof, the addresses, and the price.
POST /approve records that decision against the quote’s input_hash and total_usdc, so a changed piece or price cannot be sent under an old approval.5
Send
Save an idempotency key first, then
POST /send. The order is paid from credits, or by x402 if there are none.6
Follow the order
GET /orders/{id} returns the order and its delivery events. If the response to /send was lost, GET /orders/recover?key= finds the order by its idempotency key.Authentication
Every route needs a wallet session:X-Agent-ID and a signed x-agent-proof, which the SDK adds. A failed proof returns 401 invalid_agent_proof. The limit is 60 requests a minute per agent.
Endpoints
All paths are underhttps://win.oneshotagent.com/v1/tools/physical-mail.
Address
string
required
Recipient or sender name, up to 100 characters.
string
required
Street address, up to 200 characters.
string
Apartment, suite, or unit.
string
required
City.
string
required
Two-letter state code, for example
OH.string
required
ZIP code, five digits or ZIP+4.
string
US. No other value is accepted.Artwork
One of:- A letter:
{ "kind": "letter", "file": "<asset_id>", "color": false, "double_sided": false }. The file must be a PDF of 1 to 60 pages. - A postcard:
{ "kind": "postcard", "front": "<asset_id>", "back": "<asset_id>" }. Each side can be a PDF, PNG, or JPEG.
Approve
string
required
The quote to approve.
string
required
The quote’s
input_hash. It ties the approval to the exact addresses and artwork.string
required
The quote’s
total_usdc. It ties the approval to the exact price.boolean
required
Must be
true.Send
string
required
A key you have saved before sending, 1 to 255 characters. It never expires. Sending again with the same key and body returns the same order with
Idempotent-Replay: true.string
Refuse the send if the quote is higher than this.
string
Releases a send that an action policy held. This is separate from the quote’s
approval_id.string
required
The approved quote.
string
required
The
approval_id returned by /approve.Response
Quote
string
The quote’s ID.
string
rendering or ready.string
A hash of the normalized addresses and artwork. Pass it to
/approve.object
url of the proof (null while rendering) and thumbnails.string | null
The price, null until the quote is ready. It is the print and postage cost plus a service fee.
string | null
The service fee inside the total.
string
When the quote stops being sendable.
Order
string
The order’s ID.
string
pending, submitting, accepted, canceled, failed, or needs_reconciliation.string
Where the piece is. See delivery states.
string
The state of the payment for this order.
string
What the order cost.
object | null
The signed receipt, null until execution is final.
array
Delivery events, each with
event_id, event_type, and occurred_at.string
The time after which the piece can no longer be cancelled.
string | null
When the order was refunded, if it was.
boolean
Always
false. Delivery is a postal observation and never evidence that anyone read the piece.SDK usage
TypeScript
client.physical_mail: upload_artwork, validate_address, preview, get_quote, approve, send, get_order, recover, and cancel, each with an a-prefixed async twin.
Delivery states
fulfillment_status moves forward through accepted, mailed, in_transit, in_local_area, processed_for_delivery, and delivered. It can also be returned_to_sender, canceled, or render_failed. A late event never moves the state backwards.
processed_for_delivery is not delivered.
Refunds
A confirmed cancellation, or a failure before the piece is produced, refunds the full amount including the service fee as credits, once. Returned mail is not refunded.Limits
- US addresses only.
- First-class letters on US letter paper, and 4x6 postcards.
- A letter is a PDF of 1 to 60 pages.
- 20 MB per uploaded file.
- No HTML templates, international mail, checks, or custom envelopes.