Skip to content

Fax-IQ API

Send faxes, follow them to delivery, download the documents and read your inbound faxes over a JSON API.

Base URL

https://api.fax-iq.com

Getting started

Fax-IQ is a JSON API with one multipart endpoint. Every request goes to https://api.fax-iq.com, carries an API key, and answers JSON. An account admin creates keys in Admin-IQ, under Fax-IQ → API → Keys (not a customer yet? Request access). With one, send your first fax from the command line:

curl -X POST 'https://api.fax-iq.com/api/v1/faxes' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -F to=+12695550123 \
  -F quality=fine \
  -F file=@invoice.pdf

Authentication

Put your key in the Authorization header as a bearer token. It identifies your account, so every request is scoped to you automatically. There is no account parameter to pass, and none that would let you read anyone else's faxes.

curl 'https://api.fax-iq.com/api/v1/faxes?limit=5' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Each key carries one or both scopes, chosen when you create it. A request to an endpoint your key does not have the scope for answers 403. A key also carries a source (keys made in Admin-IQ are api), which is stamped on every fax it sends, so your reports can tell your integration's faxes from ones sent through Admin-IQ or by email.

Scope Grants
faxes:sendSend faxes and cancel ones still in progress.
faxes:readList and fetch your faxes, their event trail, their documents and your stats.
Treat a key like a password
A key can read every fax you have sent or received, download the documents, and send on your behalf. Keep it server-side, out of source control and out of a browser. If one leaks, revoke it in Admin-IQ. Revocation takes effect on the next request.

Sending, and what happens next

POST /api/v1/faxes answers 202: the fax is accepted and queued, not sent. Transmission takes from thirty seconds to several minutes, and a busy or unanswered line is retried. There are two ways to learn the outcome. Ask TAS Technologies Group to register a callback endpoint for you and you will be sent a signed fax.delivered or fax.failed event; or poll GET /api/v1/faxes/:id every few seconds, backing off after the first minute, until the status is terminal.

Status Meaning
queuedWaiting for the send worker. This is also the state between retry attempts.
processingBeing submitted to the carrier.
sendingThe carrier accepted it; transmission in progress.
deliveredSent successfully. Terminal, and definitive: page count and duration are filled in.
failedGave up after all attempts. Terminal; errorCode and errorMessage say why.
canceledCanceled by you. Terminal.
receivingAn inbound fax in progress.
receivedAn inbound fax stored and available. Terminal.

Inbound faxes appear in the same list with direction: inbound. They are also emailed to the addresses configured on the receiving number, which you can see on Fax-IQ → Numbers & settings in Admin-IQ.

Responses

A successful response is wrapped: { "success": true, "data": … }. On list endpoints, pagination sits beside data rather than inside it. GET /api/v1/health is the one exception and is not wrapped at all.

Every response carries an x-correlation-id header. Send us that value when something looks wrong, because it is how we find your request in our logs. You can set it yourself on the way in and we will echo it back.

Errors

A failure answers a JSON body with an error object. On a 400, data.fieldErrors names the field:

{
  "error": {
    "message": "Validation failed",
    "code": "VALIDATION_ERROR",
    "correlationId": "7c1f0b6e-2a94-4c3d-8f51-0d9e6a2b4c88",
    "data": { "formErrors": [], "fieldErrors": { "to": ["Invalid phone number"] } }
  }
}
Status Meaning
202A send was accepted and queued. It has not been sent yet, so watch the status.
400Validation failed (data.fieldErrors names the field), or the document is unusable because it is encrypted or over 100 pages.
401The key is missing, wrong, revoked or expired. All four look the same from outside.
403The key is valid but does not carry the scope this endpoint needs, or your account is inactive at the fax service.
404No such fax. A fax that belongs to another account also answers 404.
409The action conflicts with the fax's state, such as canceling one already delivered.
413The document is over 20 MB.
429Rate limited. Wait for the window in `x-ratelimit-reset` and retry.

Rate limits

Every response carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset. The last is the number of seconds until the window resets, not a timestamp. The ceiling is roughly 100 requests a minute per source address, so poll gently and prefer callbacks.

Endpoints

POST/api/v1/faxesfaxes:send

Send a fax

Queues a document for transmission from one of your fax numbers. Send it as multipart/form-data with the document in a part named file, or as JSON with either a public https mediaUrl or base64 content. PDF is used as-is; PNG, JPEG and plain text are converted to PDF.

Body fields

Name Type Notes
to*string

The destination fax number.

Any common format; stored as E.164

fromstring

Which number to send from. Required only when you have more than one.

One of your active fax numbers

filefile

The document, as a multipart part. Exactly one of file, mediaUrl or content.

PDF, PNG, JPEG or text; 20 MB; 100 pages

mediaUrlstring

Where to fetch the document from, in a JSON body.

Public https URL

contentstring

The document inline, in a JSON body, with contentType and filename.

base64

contentTypestring

The type of the inline document. JSON with content only.

MIME type, e.g. application/pdf

qualitystring

Transmission resolution.

standard, fine, superfine · defaults to fine

maxAttemptsinteger

How many attempts to make; overrides your account default, which is 3 unless one has been set. A busy or unanswered line, or any carrier failure other than a document or resolution problem, is retried until this is reached.

1–10 · defaults to 3

retryDelaysinteger[]

Seconds to wait before each retry, e.g. [60, 300, 900]; the last entry repeats. Overrides your account default. In multipart, pass it as a string: "60,300,900".

1–10 entries, each 10–86400 seconds

statusCallbackUrlstring

An extra callback URL for this fax only, in addition to your registered endpoints.

https URL only

metadataobject

Your own key/value pairs, echoed on the resource and in every callback. In multipart, pass it as a JSON string.

Flat, up to 20 keys of up to 64 characters; values a string (up to 512 characters), number, boolean or null

filenamestring

Display name for the document.

Up to 255 characters

sourceSystemstring

The system behind the send, for your own reporting. Runs of whitespace are collapsed to one space.

Up to 64 characters: letters, digits, spaces, dots, underscores and dashes, starting with a letter or digit

sourceReferencestring

Your own id for this fax, such as a job or document number.

Up to 255 characters

accountstring

The account number in your own system. When omitted, metadata.DATA5 is used if present.

Up to 255 characters

Request body

{
  "to": "+12695550123",
  "mediaUrl": "https://example.com/invoice.pdf",
  "quality": "fine",
  "metadata": { "invoice": "INV-1001" }
}

Example

curl -X POST 'https://api.fax-iq.com/api/v1/faxes' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "to": "+12695550123", "mediaUrl": "https://example.com/invoice.pdf", "quality": "fine", "metadata": { "invoice": "INV-1001" } }'

Response

{
  "success": true,
  "data": {
    "id": "0c0b5f2c-8d1e-4b7a-9f3c-2a6d5e8b1c04",
    "direction": "outbound",
    "status": "queued",
    "source": "api",
    "from": "+12695550100",
    "to": "+12695550123",
    "quality": "fine",
    "numPages": null,
    "attemptCount": 0,
    "maxAttempts": 3,
    "retryDelays": null,
    "originalFilename": "invoice.pdf",
    "mediaUrl": "https://api.fax-iq.com/api/v1/faxes/0c0b5f2c-8d1e-4b7a-9f3c-2a6d5e8b1c04/media",
    "createdAt": "2026-09-22T20:00:00.000Z",
    "sentAt": null,
    "completedAt": null,
    "updatedAt": "2026-09-22T20:00:00.000Z",
    "…": "the remaining fields, as on the list"
  }
}
  • The response is 202 Accepted, and it means accepted, not sent. A fax takes from thirty seconds to several minutes. Register a callback endpoint or poll GET /api/v1/faxes/:id until the status is delivered, failed or canceled.
  • A key carries its own source, so omit that field. Sending a different value is a 400.
  • Retry spacing is resolved per fax when it is created: retryDelays on the request, else your account default, else the server default (2, 4, 6 minutes…). Omit it to inherit. The value used is echoed on the resource; null means the server default.
  • A document over 20 MB answers 413, and one over 100 pages answers 400. Encrypted PDFs are rejected, and from and to must differ.
GET/api/v1/faxesfaxes:read

List faxes

Returns your faxes, newest first unless you pass sort. Every filter is optional and they combine. Results are always scoped to your account. There is no parameter that widens them.

Query parameters

Name Type Notes
limitinteger

How many faxes to return.

1–100 · defaults to 20

offsetinteger

How many to skip, for paging.

0 or more · defaults to 0

directionstring

Filter by direction.

inbound, outbound

statusstring

Filter by one or more statuses: status=failed,canceled, or repeat the parameter.

queued, processing, sending, delivered, failed, canceled, receiving, received

fromstring

Exact match on the sending number, after normalising it.

Any common format

tostring

Exact match on the destination number, after normalising it.

Any common format

sourcestring

Who submitted the fax.

api, portal, windows_client, email

sourceSystemstring

Exact match on the system named on the fax when it was sent.

sourceReferencestring

Exact match on your own id for the fax.

accountstring

Exact match on the account number.

qstring

Case-insensitive search within account, from, to and sourceReference.

Up to 100 characters

createdAfterstring

Only faxes created at or after this moment. Must not be later than createdBefore.

ISO 8601 datetime

createdBeforestring

Only faxes created at or before this moment.

ISO 8601 datetime

sortstring

Which column to sort on. Faxes without a completedAt or account sort last either way.

createdAt, completedAt, status, to, from, account · defaults to createdAt

orderstring

Sort direction.

asc, desc · defaults to desc

Example

curl -X GET 'https://api.fax-iq.com/api/v1/faxes' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": [
    {
      "id": "0c0b5f2c-8d1e-4b7a-9f3c-2a6d5e8b1c04",
      "direction": "outbound",
      "status": "delivered",
      "source": "api",
      "sourceSystem": "Billing",
      "sourceReference": "INV-1001",
      "account": "ACCT-4471",
      "from": "+12695550100",
      "to": "+12695550123",
      "quality": "fine",
      "numPages": 3,
      "attemptCount": 1,
      "maxAttempts": 3,
      "retryDelays": null,
      "errorCode": null,
      "errorMessage": null,
      "remoteStationId": null,
      "durationSeconds": 212,
      "metadata": { "invoice": "INV-1001" },
      "statusCallbackUrl": null,
      "originalFilename": "invoice.pdf",
      "mediaUrl": "https://api.fax-iq.com/api/v1/faxes/0c0b5f2c-8d1e-4b7a-9f3c-2a6d5e8b1c04/media",
      "createdAt": "2026-09-22T20:00:00.000Z",
      "sentAt": "2026-09-22T20:00:02.000Z",
      "completedAt": "2026-09-22T20:03:41.000Z",
      "updatedAt": "2026-09-22T20:03:41.000Z"
    }
  ],
  "pagination": { "total": 1, "limit": 20, "offset": 0 },
  "clientId": "3c9a77e2-0b41-4f8d-9a16-2d5e8c4b1f07"
}
  • pagination and clientId sit beside data, not inside it. clientId is always your own account.
  • Ties in the sort order are broken by createdAt and then id, so pages never overlap.
GET/api/v1/faxes/:idfaxes:read

Fetch one fax

One fax by id, with its current status and an attempts array holding one entry per carrier attempt. This is how you follow up on something you sent.

Path parameters

Name Type Notes
id*string

The fax id returned when it was accepted.

Example

curl -X GET 'https://api.fax-iq.com/api/v1/faxes/:id' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": {
    "id": "0c0b5f2c-8d1e-4b7a-9f3c-2a6d5e8b1c04",
    "direction": "outbound",
    "status": "failed",
    "from": "+12695550100",
    "to": "+12695550123",
    "attemptCount": 3,
    "maxAttempts": 3,
    "retryDelays": [60, 300],
    "errorCode": "34004",
    "errorMessage": "No answer",
    "attempts": [
      { "attemptNo": 1, "providerStatus": "no-answer", "errorCode": "34004", "errorMessage": "No answer", "numPages": null, "durationSeconds": 48, "startedAt": "2026-09-22T20:00:02.000Z", "completedAt": "2026-09-22T20:00:50.000Z" }
    ],
    "…": "the remaining fields, as on the list"
  }
}
  • A fax belonging to another account answers 404, the same as one that does not exist.
  • failed carries errorCode (a carrier code such as 34004, or PROVIDER_REJECTED when the request was refused outright) and errorMessage.
GET/api/v1/faxes/:id/eventsfaxes:read

Read the event trail

The audit trail for one fax, newest first: created, submitted, status_webhook, retry_scheduled, delivered, callback_delivered and so on. The first place to look when a callback seems to be missing.

Path parameters

Name Type Notes
id*string

The fax id.

Example

curl -X GET 'https://api.fax-iq.com/api/v1/faxes/:id/events' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": [
    { "id": "…", "event": "delivered", "level": "info", "details": null, "createdAt": "2026-09-22T20:03:41.000Z" },
    { "id": "…", "event": "submitted", "level": "info", "details": null, "createdAt": "2026-09-22T20:00:02.000Z" },
    { "id": "…", "event": "created", "level": "info", "details": null, "createdAt": "2026-09-22T20:00:00.000Z" }
  ]
}
GET/api/v1/faxes/:id/mediafaxes:read

Download the document

The stored document. By default a 302 to a signed URL valid for five minutes, which any client that follows redirects turns into the file. Two query flags change that.

Path parameters

Name Type Notes
id*string

The fax id.

Query parameters

Name Type Notes
redirectboolean

false returns { url, expiresAt, contentType, sizeBytes, filename } as JSON instead of redirecting.

defaults to true

inlineboolean

true streams the bytes through the API with Content-Disposition: inline, for a server that must not expose storage URLs.

defaults to false

Example

curl -X GET 'https://api.fax-iq.com/api/v1/faxes/:id/media' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "url": "https://….amazonaws.com/…?X-Amz-Expires=300&…",
  "expiresAt": "2026-09-22T20:10:00.000Z",
  "contentType": "application/pdf",
  "sizeBytes": 184320,
  "filename": "fax-0c0b5f2c-8d1e-4b7a-9f3c-2a6d5e8b1c04.pdf"
}
  • Outbound documents are always application/pdf. An inbound fax may be image/tiff with a .tiff filename, so read contentType or the response headers rather than assuming PDF.
  • A fax with no stored document answers 404.
POST/api/v1/faxes/:id/cancelfaxes:send

Cancel a fax

Cancels a queued fax immediately. One that is processing or sending gets a best-effort cancel at the carrier and is marked canceled. Anything else (delivered, failed, already canceled, or an inbound fax) answers 409.

Path parameters

Name Type Notes
id*string

The fax id.

Example

curl -X POST 'https://api.fax-iq.com/api/v1/faxes/:id/cancel' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": { "id": "0c0b5f2c-8d1e-4b7a-9f3c-2a6d5e8b1c04", "status": "canceled", "…": "the remaining fields" }
}
GET/api/v1/fax-numbersfaxes:read

Your fax numbers

The active fax numbers on your account, with each one's label and inbound forwarding addresses. Any of these is a valid from on a send.

Example

curl -X GET 'https://api.fax-iq.com/api/v1/fax-numbers' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": [
    { "id": "…", "phoneNumber": "+12695550100", "label": "Main office", "forwardToEmails": ["records@example.com"], "isActive": true }
  ]
}
  • Any scope may call this.
GET/api/v1/mefaxes:read

Your account

Everything about your account in one call and with no secrets: the client, the key you called with, your fax numbers, approved email senders, callback endpoints, whether the carrier is configured, the installs that send heartbeats with whether each is online, and a warnings list worth showing to whoever runs your integration.

Example

curl -X GET 'https://api.fax-iq.com/api/v1/me' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": {
    "client": { "id": "…", "cltId": 1234, "name": "Acme Medical", "isActive": true, "retryDelays": null, "maxAttempts": null, "createdAt": "…" },
    "token": { "tokenPrefix": "a1b2c3d4", "scopes": ["faxes:send", "faxes:read"], "source": "api", "expiresAt": null },
    "faxNumbers": [ { "phoneNumber": "+12695550100", "label": "Main office", "forwardToEmails": [], "isActive": true } ],
    "emailSenders": [],
    "webhookEndpoints": [],
    "provider": { "configured": true, "isActive": true },
    "agents": [ { "id": "…", "source": "windows_client", "sourceSystem": "UltraComm", "hostname": "FAX-PC", "version": "2.3.1", "status": "offline", "lastSeenAt": "…", "firstSeenAt": "…", "secondsSinceSeen": 412, "details": { "queued": 3 } } ],
    "warnings": [ { "code": "agent_offline", "message": "A client-side install has stopped sending heartbeats" } ]
  }
}
  • Any scope may call this.
  • agent_offline in warnings names the installs that have stopped sending heartbeats.
POST/api/v1/heartbeatfaxes:read

Report an install is running

Tells the fax service that a client-side install, such as the Windows client, is running. Each sourceSystem is one install; GET /me lists them with whether each is online.

Body fields

Name Type Notes
sourceSystem*string

Identifies the install within your account. Send the same value you put on its faxes.

Up to 64 characters: letters, digits, spaces, dots, underscores and dashes, starting with a letter or digit

versionstring

The install's software version.

Up to 64 characters

hostnamestring

The computer it runs on.

Up to 255 characters

detailsobject

Anything else worth showing beside the install, such as a queue length.

Up to 20 flat keys; string, number, boolean or null values

Request body

{
  "sourceSystem": "UltraComm",
  "version": "2.3.1",
  "hostname": "FAX-PC",
  "details": { "queued": 3 }
}

Example

curl -X POST 'https://api.fax-iq.com/api/v1/heartbeat' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{ "sourceSystem": "UltraComm", "version": "2.3.1", "hostname": "FAX-PC", "details": { "queued": 3 } }'

Response

{
  "success": true,
  "data": { "serverTime": "2026-10-09T20:00:00.000Z", "heartbeatIntervalS": 60 }
}
  • Any scope may call this.
  • Send one on startup and then every heartbeatIntervalS seconds from the response, even when idle. An install with no heartbeat for three minutes shows as offline.
  • Omitted fields keep their last reported value. Two computers reporting the same sourceSystem share one install, and the latest heartbeat wins.
GET/api/v1/faxes/statsfaxes:read

Read delivery stats

Counts by status for outbound and inbound faxes over a window, with pages, the delivery rate, a breakdown by source and a per-day series.

Query parameters

Name Type Notes
windowstring

How far back to count.

24h, 7d, 30d · defaults to 24h

Example

curl -X GET 'https://api.fax-iq.com/api/v1/faxes/stats' \
  -H 'Authorization: Bearer YOUR_API_KEY'

Response

{
  "success": true,
  "data": {
    "window": "24h",
    "since": "2026-09-23T20:00:00.000Z",
    "generatedAt": "2026-09-24T20:00:00.000Z",
    "clientId": "3c9a77e2-0b41-4f8d-9a16-2d5e8c4b1f07",
    "outbound": { "total": 4, "queued": 1, "processing": 0, "sending": 0, "delivered": 2, "failed": 1, "canceled": 0, "pages": 5, "deliveryRate": 0.6667 },
    "inbound": { "total": 1, "receiving": 0, "received": 1, "failed": 0, "pages": 3 },
    "bySource": [ { "source": "api", "sourceSystem": "Billing", "total": 2, "delivered": 2, "failed": 0 } ],
    "byDay": [ { "date": "2026-09-24", "outbound": 4, "inbound": 1, "delivered": 2, "failed": 1 } ]
  }
}
  • deliveryRate is delivered over delivered plus failed, and null until something has settled.
  • Cached for 15 seconds per window. Polling faster than that returns the same answer.
GET/api/v1/healthno token needed

Service health

Whether the fax service is up, with its database and storage checks. The only endpoint that needs no token, so it is safe to use from an uptime monitor.

Example

curl -X GET 'https://api.fax-iq.com/api/v1/health'

Response

{
  "status": "ok",
  "version": "0.9.0",
  "server": "fax-prod-1",
  "environment": "production",
  "checks": { "database": true, "storage": true },
  "uptime": 864213,
  "timestamp": "2026-09-24T20:00:00.000Z"
}
  • This one is not wrapped in the success/data envelope, and it answers 200 even when degraded, so read status.