Faxette

Fax API

Create a guest fax over HTTP. No API key, and no subscription. The person who opens the status link pays on that page, at the same price as the website.

Call it from your server. A successful response is wrapped in data. An error is code and message, with no data wrapper. The API does not send CORS headers, so a page on another site cannot call it from the browser.

This site, https://www.faxette.com, faxes to the US and Canada. For the EU, the UK, Switzerland, Norway, Iceland, Liechtenstein, Monaco, Montenegro, Ukraine and the Isle of Man, use https://eu.faxette.com/api.

Errors

message is one sentence. Branch on code, which stays stable.

{
  "code": "validation_failed",
  "message": "Fax number is in a country we can't fax to yet."
}
code Status When
validation_failed 422 A field is missing or refused. message is one sentence.
unauthorized 401 A request that needs a token was sent without one.
not_found 404 Unknown fax, or a token that does not match. Both look the same.
rate_limited 429 Too many upload slots or fax creates. The message says to wait a minute.

Upload a document

POST /api/v1/documents

Ask for an upload slot, then PUT the file bytes to upload_url. The bytes go straight to storage. This request only checks what you say the file is. A fax is created afterwards with the slot's id. There is no request to download a document, or to list them.

checksum is the file's MD5, base64-encoded, the same value as a Content-MD5 header. byte_size is the exact size in bytes. content_type is one of application/pdf, image/jpeg, image/png, image/webp, image/heic, and image/heif. The slot's id works for 1 hour. The upload URL works for 5 minutes. Storage refuses a file whose size or MD5 does not match the slot.

File size and checksum
openssl dgst -md5 -binary referral.pdf | openssl base64
wc -c referral.pdf | awk '{print $1}'
Request
curl -s https://www.faxette.com/api/v1/documents \
  -H "Content-Type: application/json" \
  -d '{"filename":"referral.pdf","byte_size":248113,"checksum":"gbgEgwqIGW9JMTqqgYW+xw==","content_type":"application/pdf"}'

201, with the slot.

Response
{
  "data": {
    "type": "Document",
    "id": "SIGNED_ID",
    "upload_url": "https://example.r2.cloudflarestorage.com/faxette/...",
    "upload_headers": {
      "Content-Type": "application/pdf",
      "Content-MD5": "gbgEgwqIGW9JMTqqgYW+xw==",
      "Content-Disposition": "inline; filename=\"referral.pdf\"; filename*=UTF-8''referral.pdf"
    }
  }
}

PUT the raw bytes to upload_url, with every header in upload_headers. Copy the values from the response. $UPLOAD_URL below stands in for upload_url.

Upload
curl -X PUT --data-binary @referral.pdf \
  -H "Content-Type: application/pdf" \
  -H "Content-MD5: gbgEgwqIGW9JMTqqgYW+xw==" \
  -H "Content-Disposition: inline; filename=\"referral.pdf\"; filename*=UTF-8''referral.pdf" \
  "$UPLOAD_URL"

Create a fax

POST /api/v1/faxes

Creating a fax does not charge a card. The response includes token once. Store it. It is not returned again. Poll GET /api/v1/faxes/:id until status is documents_ready and checkout_available is true, then send the person to status_url. They check the fax number, documents and price, and pay there. Payment starts from the Pay button on that page. A link that started checkout would be opened by chat apps' link previews, so there is no checkout endpoint.

Sending this request twice creates two faxes. There is no idempotency key.

With document ids

documents is the slot ids, in the order they should be faxed. An id already attached to a fax cannot be used on another one. Upload the file again.

Request
curl -s https://www.faxette.com/api/v1/faxes \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","fax_number":"+14155552671","documents":["SIGNED_ID"]}'

With the files in one request

Send documents[] as multipart files, in fax order. The bytes come through this server, so a large file is better sent with an upload slot. The file's content type has to be one of the types above. type= sets it when the filename would not.

Request
curl -s https://www.faxette.com/api/v1/faxes \
  -F "[email protected]" \
  -F "fax_number=+14155552671" \
  -F "documents[][email protected];type=application/pdf" \
  -F "documents[][email protected];type=image/jpeg"

An empty draft

Leave documents out. The response's status is draft, and the person adds the files on the status page.

Request
curl -s https://www.faxette.com/api/v1/faxes \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","fax_number":"+14155552671"}'

Fields

Field What to send
email Required. Where we email the receipt and updates. The address has to be able to receive mail. Disposable inboxes are refused.
fax_number Required. A number without a country code is read as a US number. E.164 is +14155552671. Premium-rate numbers are refused. Numbers outside the countries this site faxes to are refused.
documents Optional. Slot ids, or multipart files. Up to 10, each up to 20 MB (20,971,520 bytes).

201, with the fax and its token. Other fields are null until the fax moves on.

Response
{
  "data": {
    "type": "Fax",
    "id": "k3n8q0w1x4y7",
    "status": "processing_documents",
    "fax_number": "+14155552671",
    "page_count": null,
    "retries_remaining": 2,
    "next_attempt_at": null,
    "resend_by": null,
    "updated_at": "2026-10-27T16:04:00.000Z",
    "status_url": "https://www.faxette.com/f/k3n8q0w1x4y7?token=exampletoken",
    "checkout_available": false,
    "price": null,
    "payment": null,
    "attempt": 1,
    "max_attempts": 3,
    "document_errors": [],
    "failure_reason": null,
    "token": "exampletoken"
  }
}

Once the pages are counted, those fields look like this. currency on this site is usd.

After the page count
{
  "status": "documents_ready",
  "page_count": 3,
  "checkout_available": true,
  "price": { "amount_cents": 500, "currency": "usd" }
}

Read a fax

GET /api/v1/faxes/:id?token=

Returns the same object as create, without token. The response never includes the documents or their filenames. Poll it while status is processing_documents. document_errors says which file to fix, by its position in the fax, starting at 1.

Request
curl -s "https://www.faxette.com/api/v1/faxes/k3n8q0w1x4y7?token=exampletoken"
A file that needs fixing
{
  "document_errors": [
    {
      "reason": "password_protected",
      "position": 2,
      "message": "Document 2 is locked with a password. Open it, save or print it as a new PDF without the password, then upload that copy."
    }
  ]
}

Replace the documents

PATCH /api/v1/faxes/:id/documents?token=

documents is the full list, in fax order. To keep a file, send its slot id again. To drop one, leave it out. Dropped files are deleted, and the pages are counted again. This works while status is draft, processing_documents or documents_ready. Once checkout has started, the files are locked. Start a new fax to change them. Slot ids work for 1 hour, and the fax response never includes them, so keep the ids you uploaded.

Request
curl -s -X PATCH "https://www.faxette.com/api/v1/faxes/k3n8q0w1x4y7/documents?token=exampletoken" \
  -H "Content-Type: application/json" \
  -d '{"documents":["SIGNED_ID","ANOTHER_ID"]}'

200, with the fax. status goes back to processing_documents.

Send it again

POST /api/v1/faxes/:id/resend?token=

A failed fax can be sent again, free, while the card is still held. That window is 48 hours, and resend_by is when it ends. retries_remaining has to be above 0. It starts at 2. failure_reason of our_side cannot be resent. The hold is released straight away.

fax_number is optional. Leave it out, or send it blank, to use the same number. A number without a country code is read in the country of the fax's current number.

Request
curl -s -X POST "https://www.faxette.com/api/v1/faxes/k3n8q0w1x4y7/resend?token=exampletoken" \
  -H "Content-Type: application/json" \
  -d '{"fax_number":"+14155550199"}'

200, with the fax. status is scheduled.

A fax that can be resent
{
  "status": "failed",
  "failure_reason": "temporary",
  "retries_remaining": 2,
  "resend_by": "2026-10-29T16:04:00.000Z",
  "payment": { "status": "authorized", "amount_cents": 500, "tax_cents": 0, "currency": "usd" }
}

The fax object

Field Type Meaning
type string Always "Fax".
id string 12 lowercase letters or digits.
status string Where the fax is. See the status table.
fax_number string E.164, like +14155552671.
page_count integer or null Null until the pages are counted.
retries_remaining integer Customer resends left. Starts at 2. Busy-line retries do not use this.
next_attempt_at time or null When a busy line will be dialed again.
resend_by time or null When a failed fax can no longer be resent, and the hold is released.
updated_at time When the fax last changed.
status_url string The page where the sender pays. It carries the token.
checkout_available boolean True when that page can take payment.
price object or null Before tax: amount_cents and currency. Null until the pages are counted. Extra pages are already included.
payment object or null Null until the card is held. Then status, amount_cents, tax_cents and currency.
attempt integer Which dial of this send, from 1 to max_attempts.
max_attempts integer Always 3.
document_errors array What to fix before the fax can be sent. Empty when there is nothing to fix.
failure_reason string or null Set when status is failed.
token string Only on create. An opaque string. Store it.

status

status Meaning
draft Files are missing, or a file needs fixing. See document_errors.
processing_documents Counting pages.
documents_ready Priced. The status page can take payment.
awaiting_payment Checkout has started.
scheduled Paid, and waiting to send. On a busy-line retry, next_attempt_at is set.
sending On the line.
delivered The receiving machine confirmed it. The card is charged.
failed See failure_reason. The card stays held when the fax can be resent.
canceled Unpaid for 24 hours. The files are deleted.

A busy line is dialed again after 5 minutes, then 15 minutes. Those dials are attempt of max_attempts. They are separate from a customer resend.

payment

payment.status is authorized while the card is held, paid once the fax is delivered and the hold is charged, canceled when the hold is released, or refunded if the charge was refunded. amount_cents is what the card was held or charged for, tax included. This site does not add tax, so tax_cents is 0. price stays the amount before tax.

failure_reason

Null unless status is failed.

temporary The line was busy, did not answer, or dropped the call, after 3 attempts. The fax can be resent.
wrong_number The number did not reach a fax machine. Resend it to a corrected number.
our_side Something failed on our side. The hold is released, and the fax cannot be resent.

document_errors

Each item has reason, position and message. position is the document's number in the fax, and null when the problem is about the whole fax. message is the sentence to show a person. The same problems can also come back as validation_failed on create, before a fax exists.

password_protected The PDF has a password.
unreadable_pdf The PDF could not be opened.
unreadable_image The image could not be opened.
unsupported_type Not a PDF or a photo we can fax.
image_too_large Over 50 megapixels.
multi_page_image An image file with more than one page. Save it as a PDF.
too_many_pages Over 200 pages.
too_many_documents Over 10 files.
document_too_large One file is over 20 MB.
no_documents The fax has no files.
build_failed We could not assemble the fax. Uploading the files again is the next step.

Limits

For Claude, ChatGPT, and other assistants, See the MCP server β†’