unofax
API reference

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:

  1. Create a job with the destination number and file name.
  2. Upload the document to the pre-signed URL you get back.
  3. Poll the job until it is ready_to_send, then read the quoted price.
  4. Pay and send: take the 402 challenge, sign it, retry.
Pricing. $0.20 USDC per page, gas included. Cover sheets are free and don't count toward the page total. Faxes go to 45 countries.

Payments

Payment happens on the send endpoint using the standard x402 handshake:

  1. Call the endpoint without payment. It responds 402 Payment Required with a base64-encoded JSON challenge in the PAYMENT-REQUIRED header.
  2. Sign one of the entries in accepts with an x402 client and your wallet.
  3. Repeat the identical request with the base64 payload in a PAYMENT-SIGNATURE header.

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.

Request flow
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 failed

Errors

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 RequestInvalid input: missing faxNumber, bad E.164 format, unsupported file type, cover sheet validation, or a malformed PAYMENT-SIGNATURE.
402 Payment RequiredReturned by the send endpoint until a valid payment is attached.
404 Not FoundUnknown or expired jobId.
409 ConflictThe job isn't ready to send yet, or its quote expired. Poll the job and retry.
503 Service Unavailablex402 payments are temporarily unavailable. Retry later.
400Error response
{
  "error": "faxNumber must be in E.164 format"
}

Job statuses

awaiting_uploadJob created, waiting for the document.
processingDocument received and converting to fax format. Usually 5 to 15 seconds.
ready_to_sendConverted and priced. The job includes payment and sendUrl.
sendingPaid. The fax is dialing, transmitting, or waiting for its scheduledTime.
retryingAn attempt didn't go through (for example, the line was busy). It will be retried automatically.
sentDelivered. Final.
failedCould not be delivered. Final. See error, and contact support@unofax.com with the jobId.
Lifecycle
awaiting_upload
  → processing
  → ready_to_send
  → sending ⇄ retrying
  → sent | failed

The job object

The shape varies by status: fields appear as the job progresses.

Attributes

  • jobIdstring
    Unique identifier for the job.
  • faxNumberstring
    Destination fax number in E.164 format.
  • statusenum
    Where the job is in its lifecycle. See Job statuses.
  • pageCountinteger
    Number of fax pages after conversion, excluding the cover sheet. Present once the document has been processed.
  • previewUrlstring
    Relative URL of a PDF preview of what will be transmitted, when available.
  • scheduleobject
    Present when the job was created with scheduledTime.
  • paymentobject
    Only when status is ready_to_send. The exact amount and destination for the x402 payment.
  • sendUrlstring
    Only when status is ready_to_send. Relative URL to POST to for payment and sending.
  • errorstring
    Only when status is failed. Failure message when available.
The job object
{
  "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

  • faxNumberstringRequired
    Destination fax number in E.164 format, e.g. +14155551212.
  • fileNamestringRequired
    File name with extension. Used to infer the MIME type when mimeType is omitted.
  • mimeTypestring
    Explicit MIME type, e.g. application/pdf.
  • scheduledTimestring
    RFC 3339 timestamp. If omitted, the fax is sent as soon as payment settles.
  • coverSheetobject
    Free 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.

POST/api/x402
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"
    }
  }'
200Response
{
  "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.

PUT{uploadUrl}
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @contract.pdf

Retrieve 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

  • jobIdstringRequired
    The job ID returned by create.

Returns

A job object, or 404 if the ID is unknown or expired.

GET/api/x402/:jobId
curl https://unofax.com/api/x402/019d7a7f-a183-752d-9b3b-11511179033b
200Response
{
  "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

  • jobIdstringRequired
    The job to send.

Headers

  • PAYMENT-SIGNATUREstring
    Base64-encoded x402 payment payload. Omit it on the first call to receive the 402 challenge.

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.

POST/api/x402/:jobId/send
# 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"
402Challenge
{
  "error": "payment required"
}

// PAYMENT-REQUIRED header, base64-decoded
{
  "x402Version": 2,
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:8453",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "amount": "600000",
    "payTo": "0x1a2b..."
  }]
}
202Paid
{
  "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.

GET/api/x402
curl https://unofax.com/api/x402
200Response
{
  "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.

Open openapi.json

GET/api/x402/openapi.json
curl https://unofax.com/api/x402/openapi.json