x402 Fax API
Send a fax from any HTTP client and pay per page in USDC on Base. There are no accounts or API keys: the x402 protocol turns the payment itself into authorisation.
Overview
The API is organised around a single resource, the fax job. It uses JSON request and response bodies and standard HTTP status codes. Every request is unauthenticated except the one that sends the fax, which carries a signed x402 payment.
A complete send takes four steps:
- Create a job with the destination number and file name.
- Upload the document to the pre-signed URL you get back.
- Poll the job until it is
ready_to_send, then read the quoted price. - Pay and send: take the
402challenge, sign it, retry.
https://unofax.comPayments
Payment happens on the send endpoint using the standard x402 handshake:
- Call the endpoint without payment. It responds
402 Payment Requiredwith a base64-encoded JSON challenge in thePAYMENT-REQUIREDheader. - Sign one of the entries in
acceptswith an x402 client and your wallet. - Repeat the identical request with the base64 payload in a
PAYMENT-SIGNATUREheader.
Settlement is on Base (eip155:8453) in USDC. The quote in the job's payment object is valid until expiresAt. The send endpoint is idempotent: replaying a paid request never charges twice, and the settlement receipt comes back in the PAYMENT-RESPONSE header.
POST /api/x402 # create job
PUT {uploadUrl} # upload file
GET /api/x402/{jobId} # until ready_to_send
POST /api/x402/{jobId}/send # 402 challenge
POST /api/x402/{jobId}/send # + PAYMENT-SIGNATURE
GET /api/x402/{jobId} # until sent or failedErrors
Codes in the 2xx range mean success. 402 is an expected step in the payment flow, not a failure. Other 4xx codes mean the request can't be fulfilled as sent. Error bodies have a single human-readable error field.
400 Bad Request | Invalid input: missing faxNumber, bad E.164 format, unsupported file type, cover sheet validation, or a malformed PAYMENT-SIGNATURE. |
|---|---|
402 Payment Required | Returned by the send endpoint until a valid payment is attached. |
404 Not Found | Unknown or expired jobId. |
409 Conflict | The job isn't ready to send yet, or its quote expired. Poll the job and retry. |
503 Service Unavailable | x402 payments are temporarily unavailable. Retry later. |
{
"error": "faxNumber must be in E.164 format"
}Job statuses
awaiting_upload | Job created, waiting for the document. |
|---|---|
processing | Document received and converting to fax format. Usually 5 to 15 seconds. |
ready_to_send | Converted and priced. The job includes payment and sendUrl. |
sending | Paid. The fax is dialing, transmitting, or waiting for its scheduledTime. |
retrying | An attempt didn't go through (for example, the line was busy). It will be retried automatically. |
sent | Delivered. Final. |
failed | Could not be delivered. Final. See error, and contact support@unofax.com with the jobId. |
awaiting_upload
→ processing
→ ready_to_send
→ sending ⇄ retrying
→ sent | failedThe job object
The shape varies by status: fields appear as the job progresses.
Attributes
jobIdstringUnique identifier for the job.faxNumberstringDestination fax number in E.164 format.statusenumWhere the job is in its lifecycle. See Job statuses.pageCountintegerNumber of fax pages after conversion, excluding the cover sheet. Present once the document has been processed.previewUrlstringRelative URL of a PDF preview of what will be transmitted, when available.scheduleobjectPresent when the job was created withscheduledTime.paymentobjectOnly whenstatusisready_to_send. The exact amount and destination for the x402 payment.sendUrlstringOnly whenstatusisready_to_send. Relative URL to POST to for payment and sending.errorstringOnly whenstatusisfailed. Failure message when available.
{
"jobId": "019d7a7f-a183-752d-9b3b-11511179033b",
"faxNumber": "+14155551212",
"status": "ready_to_send",
"pageCount": 3,
"previewUrl": "/preview/019d7a7f-a183-752d-9b3b-11511179033b/contract.pdf",
"payment": {
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amountAtomic": "600000",
"amountDisplay": "0.6 USDC",
"payTo": "0x1a2b...",
"expiresAt": "2026-09-29T12:30:00Z"
},
"sendUrl": "/api/x402/019d7a7f-a183-752d-9b3b-11511179033b/send"
}Create a fax job
Creates a job and returns a pre-signed uploadUrl. Each job takes a single document; to fax several, merge them into one PDF first.
Parameters
faxNumberstringRequiredDestination fax number in E.164 format, e.g.+14155551212.fileNamestringRequiredFile name with extension. Used to infer the MIME type whenmimeTypeis omitted.mimeTypestringExplicit MIME type, e.g.application/pdf.scheduledTimestringRFC 3339 timestamp. If omitted, the fax is sent as soon as payment settles.coverSheetobjectFree cover page prepended to the fax. It does not count toward the page price.
Returns
The new job with status: "awaiting_upload", plus uploadUrl, statusUrl and sendUrl.
curl -X POST https://unofax.com/api/x402 \
-H "Content-Type: application/json" \
-d '{
"faxNumber": "+14155551212",
"fileName": "contract.pdf",
"coverSheet": {
"senderName": "Jane Smith",
"recipientName": "Acme Legal",
"subject": "Signed contract"
}
}'{
"jobId": "019d7a7f-a183-752d-9b3b-11511179033b",
"status": "awaiting_upload",
"uploadUrl": "https://unofax.com/uploads/...?Policy=...&Signature=...",
"statusUrl": "/api/x402/019d7a7f-a183-752d-9b3b-11511179033b",
"sendUrl": "/api/x402/019d7a7f-a183-752d-9b3b-11511179033b/send"
}Upload the document
PUT the raw file bytes to the uploadUrl from the create call. Don't wrap them in JSON or multipart. Conversion starts as soon as the upload completes.
Accepted formats: PDF, Word, PNG, JPG, GIF, WebP, HEIC and TIFF, up to 100 MB.
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--data-binary @contract.pdfRetrieve a job
Returns the current state of a job. Poll every 2 to 3 seconds after uploading until status is ready_to_send, then every 5 seconds after paying until it is sent or failed. Most faxes finish in 1 to 2 minutes per page.
Path parameters
jobIdstringRequiredThe job ID returned by create.
Returns
A job object, or 404 if the ID is unknown or expired.
curl https://unofax.com/api/x402/019d7a7f-a183-752d-9b3b-11511179033b{
"jobId": "019d7a7f-a183-752d-9b3b-11511179033b",
"faxNumber": "+14155551212",
"status": "ready_to_send",
"pageCount": 3,
"previewUrl": "/preview/019d7a7f-a183-752d-9b3b-11511179033b/contract.pdf",
"payment": {
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amountAtomic": "600000",
"amountDisplay": "0.6 USDC",
"payTo": "0x1a2b...",
"expiresAt": "2026-09-29T12:30:00Z"
},
"sendUrl": "/api/x402/019d7a7f-a183-752d-9b3b-11511179033b/send"
}Pay and send
Pays for a ready_to_send job and queues it for transmission, or for its scheduledTime. See Payments for the handshake.
Path parameters
jobIdstringRequiredThe job to send.
Headers
PAYMENT-SIGNATUREstringBase64-encoded x402 payment payload. Omit it on the first call to receive the402challenge.
Returns
202 with status: "sending" once payment is accepted. Replaying the same paid request returns 202 while the fax is in flight and 200 with status: "sent" after delivery. 409 if the job isn't ready or the quote expired.
# 1. Request the payment challenge
curl -i -X POST https://unofax.com/api/x402/$JOB_ID/send
# 2. Retry with the signed x402 payment payload
curl -X POST https://unofax.com/api/x402/$JOB_ID/send \
-H "PAYMENT-SIGNATURE: $PAYMENT_SIGNATURE"{
"error": "payment required"
}
// PAYMENT-REQUIRED header, base64-decoded
{
"x402Version": 2,
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "600000",
"payTo": "0x1a2b..."
}]
}{
"jobId": "019d7a7f-a183-752d-9b3b-11511179033b",
"status": "sending"
}API descriptor
A compact, machine-readable summary of the endpoints and the expected call order. Point an agent here and it can work out the rest on its own.
curl https://unofax.com/api/x402{
"name": "unofax x402 API",
"version": "v1",
"openapi": "/api/x402/openapi.json",
"create": { "method": "POST", "path": "/api/x402" },
"status": { "method": "GET", "path": "/api/x402/{jobId}" },
"send": { "method": "POST", "path": "/api/x402/{jobId}/send" },
"flow": [
"POST /api/x402",
"PUT uploadUrl",
"GET /api/x402/{jobId} until status=ready_to_send",
"POST /api/x402/{jobId}/send",
"retry same POST with PAYMENT-SIGNATURE after 402",
"GET /api/x402/{jobId} until status=sent, retrying, or failed"
]
}OpenAPI spec
The full OpenAPI 3 document for this API, including every response shape. Use it to generate a client or to give an LLM agent the complete schema as a tool definition.
curl https://unofax.com/api/x402/openapi.json