SendFAXMail
REST API · v1

Send FAX Mail API Reference

Send and track faxes, and list your dedicated numbers, over a simple JSON REST API. Every request is authenticated with an API key and runs the same quota, HIPAA-BAA, and number gates as the dashboard.

Base URL & authentication

All endpoints live under the base URL:

https://www.sendfaxmail.com

Authenticate every request with a bearer token — your API key, created at Dashboard → Developers (shown once on creation, keys are prefixed sfm_live_).

Authorization: Bearer sfm_live_your_key

Business plan feature. The REST API and webhooks are available on the Business and Enterprise plans. Requests from keys on lower plans return 403 plan_upgrade_required. View plans.

Scopes

Each key is granted one or more scopes. A request to an endpoint whose scope the key lacks returns 403 forbidden.

ScopeGrants
faxes:sendSend outbound faxes (POST /v1/faxes).
faxes:readList and retrieve faxes.
numbers:readList your dedicated fax numbers.
contacts:readList and retrieve address-book contacts.
contacts:writeCreate and delete address-book contacts.
scheduled:readList and retrieve scheduled (send-later) faxes.
scheduled:writeSchedule and cancel send-later faxes.

Idempotency & rate limits

Idempotency

Send an Idempotency-Key header on POST /v1/faxes to safely retry a send. A replay with the same key returns the original fax instead of sending twice.

Rate limits

Sends are limited to 60 requests per minute per key. Over the limit returns 429 rate_limited — back off and retry.

Endpoints

POST/v1/faxes

Send a fax

Send an outbound fax. Provide the document inline as base64 (`document.content` + `document.content_type`) or as a public HTTPS link (`document_url`). Runs the same quota, HIPAA-BAA, and number gates as the dashboard. Returns the created fax resource.

Scope: faxes:sendSupports Idempotency-Key
curl https://www.sendfaxmail.com/v1/faxes \
  -H "Authorization: Bearer sfm_live_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"to":"+12125551234","document":{"content":"<base64-pdf>","content_type":"application/pdf"}}'
GET/v1/faxes

List faxes

List your account's faxes, newest first. Supports `limit` (1–100, default 25) and `page` (default 1) query params. Soft-deleted faxes are excluded.

Scope: faxes:read
curl "https://www.sendfaxmail.com/v1/faxes?limit=25&page=1" \
  -H "Authorization: Bearer sfm_live_your_key"
GET/v1/faxes/{id}

Retrieve a fax

Retrieve one fax by id — poll this to track delivery status (`queued` → `sending` → `delivered`/`failed`).

Scope: faxes:read
curl https://www.sendfaxmail.com/v1/faxes/fax_id \
  -H "Authorization: Bearer sfm_live_your_key"
GET/v1/numbers

List fax numbers

List the active dedicated fax numbers on your account.

Scope: numbers:read
curl https://www.sendfaxmail.com/v1/numbers \
  -H "Authorization: Bearer sfm_live_your_key"
GET/v1/contacts

List contacts

List your address-book contacts (newest first).

Scope: contacts:read
curl https://www.sendfaxmail.com/v1/contacts \
  -H "Authorization: Bearer sfm_live_your_key"
POST/v1/contacts

Create a contact

Add an address-book contact (`name` + `fax_number`, optional `company`). Fax numbers are unique per account.

Scope: contacts:write
curl https://www.sendfaxmail.com/v1/contacts \
  -H "Authorization: Bearer sfm_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{"name":"Dr. Smith","fax_number":"+12125551234"}'
DELETE/v1/contacts/{id}

Delete a contact

Delete one address-book contact by id.

Scope: contacts:write
curl -X DELETE https://www.sendfaxmail.com/v1/contacts/contact_id \
  -H "Authorization: Bearer sfm_live_your_key"
GET/v1/scheduled

List scheduled faxes

List your scheduled (send-later) faxes, newest first.

Scope: scheduled:read
curl https://www.sendfaxmail.com/v1/scheduled \
  -H "Authorization: Bearer sfm_live_your_key"
POST/v1/scheduled

Schedule a fax

Queue a fax to send at a future `send_at` (ISO 8601, 60s–180 days out). Same body as POST /v1/faxes plus `send_at`. The document is normalized to a fax-ready PDF and stored; it sends through the same gates at the scheduled time.

Scope: scheduled:writeSupports Idempotency-Key
curl https://www.sendfaxmail.com/v1/scheduled \
  -H "Authorization: Bearer sfm_live_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"to":"+12125551234","document":{"content":"<base64-pdf>","content_type":"application/pdf"},"send_at":"2026-07-01T17:00:00Z"}'
GET/v1/scheduled/{id}

Retrieve a scheduled fax

Retrieve one scheduled fax by id (status `pending` → `sent`/`failed`/`canceled`).

Scope: scheduled:read
curl https://www.sendfaxmail.com/v1/scheduled/sched_id \
  -H "Authorization: Bearer sfm_live_your_key"
DELETE/v1/scheduled/{id}

Cancel a scheduled fax

Cancel a still-`pending` scheduled fax. A row already claimed/sent by the dispatcher can't be canceled (409).

Scope: scheduled:write
curl -X DELETE https://www.sendfaxmail.com/v1/scheduled/sched_id \
  -H "Authorization: Bearer sfm_live_your_key"

Errors

Errors return a non-2xx status and a JSON body of the shape { "error": { "type", "message" } }.

StatusTypeWhen
400invalid_requestThe request body or a field is missing or invalid.
401unauthorizedMissing or invalid API key.
403plan_upgrade_requiredThe key owner's plan is below Business — the API is a Business+ feature.
403forbiddenThe key lacks the scope required for this endpoint.
404not_foundNo resource with that id on your account.
405method_not_allowedThe HTTP method isn't allowed on this route.
422send_failedThe send was rejected by a gate (quota reached, BAA required, no active number).
429rate_limitedPer-key rate limit exceeded — back off and retry.

Webhook signature verification

Register outbound webhook endpoints at Dashboard → Developers to receive fax.delivered, fax.failed, fax.received events. Each delivery is signed with HMAC-SHA256 and carries a X-SFM-Signature header in the form t=<unix-timestamp>,v1=<hex-hmac>. The signature is computed over `${t}.${rawBody}` using your endpoint's signing secret. Verify against the raw request body — do not re-serialize the parsed JSON.

import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody = the exact bytes you received (do NOT JSON.parse then re-stringify)
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(",").map((kv) => kv.split("=")),
  );
  const expected = createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 || "");
  return a.length === b.length && timingSafeEqual(a, b);
}

Ready to build?

Create an API key and send your first fax programmatically in minutes.

Get an API key