# Faxes API reference

> The fax job operations of the Fax for Agents API. Send a quoted fax, read its state and attempts, cancel it while that is possible, and fetch the receipt.

Page: https://faxforagents.com/docs/api/reference/faxes/
Last changed: 2026-11-13

Updated Nov 13, 2026

8 operations of the Fax for Agents API, from the OpenAPI document version 1.20.0, snapshot of 2026-11-13. The base URL is `https://api.faxforagents.com/v1`. This is one group of the [full reference](https://faxforagents.com/docs/api/reference/).

- POST /v1/faxes sends a quoted fax, and its only required field is quote_id.
- Cancelling a fax is best effort once sending has started.
- The receipt of a fax is served as JSON and as the same facts in a one-page PDF.

## Operations

- `GET /v1/faxes` List fax jobs, newest first
- `POST /v1/faxes` Send a quoted fax (Idempotency-Key recommended)
- `GET /v1/faxes/{fax_id}` Read a fax job (with evidence once delivered)
- `GET /v1/faxes/{fax_id}/attempts` Attempt history
- `POST /v1/faxes/{fax_id}/cancel` Cancel if still possible (best effort once sending)
- `GET /v1/faxes/{fax_id}/events` Ordered events of one fax
- `GET /v1/faxes/{fax_id}/receipt` Delivery evidence as a receipt (JSON)
- `GET /v1/faxes/{fax_id}/receipt.pdf` The same receipt as a one-page PDF

## How is a fax sent?

`POST /v1/faxes` with a `quote_id`. The quote already holds the document, the destination, the sender and the price, so the send repeats none of them. Two optional fields ride along. `approval_id` is an approved request bound to this quote, and this one send consumes it. `client_reference` is your own label for the job.

The answer is the job, in state `queued`. Sending is asynchronous. The call returns as soon as the job is written, and the outcome arrives later on the job and as an event.

## Why send an Idempotency-Key?

The summary of the operation recommends the `Idempotency-Key` header. A send that times out leaves you not knowing whether a job exists. Repeat the call with the same key and the answer is the same job, not a second fax. The list also filters by `idempotency_key`, so a client can look a job up by the key it used.

## Which states can a job be in?

Six: `queued`, `active`, `delivered`, `partial`, `failed` and `unknown`. `GET /v1/faxes/{fax_id}` returns the state with `pages_sent`, `attempt_count`, `captured_micro` and, when the fax failed, a `failure_code`. A delivered job carries `evidence`, the carrier's report of the delivery.

`delivered` is said only with that evidence. `unknown` means no outcome was reported in time. A job in `unknown` or `partial` is never sent again by the service on its own. The [errors page](https://faxforagents.com/docs/errors/) explains each state and what it means for the money held.

## How do I follow one fax in detail?

Three reads. `GET /v1/faxes/{fax_id}/attempts` is the attempt history, one row per try. `GET /v1/faxes/{fax_id}/events` is the ordered list of events of this fax. `GET /v1/faxes` lists jobs newest first and filters by `state`.

For every fax at once, use webhooks, or walk the log in the [events group](https://faxforagents.com/docs/api/reference/events/).

## Can a fax be cancelled?

`POST /v1/faxes/{fax_id}/cancel` cancels if that is still possible. A queued job is cancelled at once. Once sending has started the cancel is best effort, as the summary says: the carrier may already have the pages. A fax that has ended answers 409 `not_cancelable`. The call needs `fax:cancel`.

## What is the receipt?

`GET /v1/faxes/{fax_id}/receipt` returns the delivery evidence as JSON once the fax has ended. It has the state, the numbers, `pages_sent`, the amount captured, the hash of the document and of the transmitted copy, the attempts and a `note`. `GET /v1/faxes/{fax_id}/receipt.pdf` is the same receipt as a one-page PDF, for a file or a person.

The receipt is the carrier's report, not legal proof of acceptance, and the PDF says so.

## What can refuse a send?

A send answers 402 when the wallet cannot cover the quote. It answers 403 when a live API key has neither an approval nor a policy that allows the send. It answers 409 with `quote_expired` or `quote_consumed` when the quote can no longer be sent, and with an approval code when the approval does not match. A refused send creates no job.

## Which scopes does the group use?

`fax:send` for the send, `fax:cancel` for the cancel and `fax:read` for every read, the receipt included. The default API key carries all three. A read-only API key keeps `fax:read` and drops the other two.

## Each operation in detail

### GET `/v1/faxes`

List fax jobs, newest first

Credential: an API key or a signed-in session. Scope: `fax:read`.

#### Parameters

GET /v1/faxes parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `state` | query | string or null | no |  |
| `idempotency_key` | query | string or null | no | Find the send job using its Idempotency-Key. This lookup requires the stored replay record. An empty result does not prove no fax was sent. |
| `cursor` | query | string or null | no |  |
| `limit` | query | integer | no | Default `50` . |

#### Responses

GET /v1/faxes responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Response fields

GET /v1/faxes response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | array of object FaxJob | yes |  |
| `next_cursor` | string or null | no |  |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes \ -H "Authorization: Bearer ffa_test_..."`
```

### POST `/v1/faxes`

Send a quoted fax (Idempotency-Key recommended)

Credential: an API key or a signed-in session. Scope: `fax:send`.

#### Parameters

POST /v1/faxes parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string | no | Replay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay. |

#### Request body application/json, required

M9: an approved request authorizes the send; without one a quote that needs authorization is evaluated against the caller's delegated policies.

POST /v1/faxes request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `approval_id` | string or null | no | An approved approval request bound to this quote. Consumed by this one send. |
| `client_reference` | string or null | no |  |
| `quote_id` | string | yes |  |

#### Responses

POST /v1/faxes responses

| Status | Meaning |
| --- | --- |
| `201` | Successful Response |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `402` | The prepaid balance does not cover the call: insufficient_balance. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `404` | not_found: no such resource for this credential. A resource of another organization answers the same. |
| `409` | The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Response fields

POST /v1/faxes response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount_micro` | integer | yes |  |
| `attempt_count` | integer | yes |  |
| `billable_pages` | integer | yes |  |
| `cancel_requested_at` | string or null | yes |  |
| `captured_micro` | integer | yes |  |
| `client_reference` | string or null | yes |  |
| `completed_at` | string or null | yes |  |
| `cover_pages` | integer | yes |  |
| `created_at` | string | yes |  |
| `currency` | string | yes |  |
| `delete_after_send` | boolean | no |  |
| `direction` | string | yes |  |
| `document_id` | string | yes |  |
| `document_sha256` | string | yes |  |
| `environment` | string (live, test) | yes |  |
| `evidence` | object or null | no | The carrier's evidence once delivered or partial; null before. |
| `failure_code` | string or null | yes |  |
| `from` | string or null | no |  |
| `id` | string | yes |  |
| `one_time` | object OneTimeJob or null | no |  |
| `one_time_order_id` | string or null | no | The one-time order this fax was paid by; null for a wallet fax. |
| `pages_sent` | integer | yes |  |
| `provider` | string | yes |  |
| `provider_ref` | string or null | yes |  |
| `quality` | string | no |  |
| `quote_id` | string or null | yes |  |
| `route` | string | yes |  |
| `sender_name` | string or null | no |  |
| `state` | string (queued, active, partial, failed, unknown, delivered) | yes |  |
| `to` | string | yes |  |
| `transmitted_sha256` | string or null | yes |  |
| `updated_at` | string | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes \ -X POST \ -H "Authorization: Bearer ffa_test_..." \ -H "Content-Type: application/json" \ -d '{"quote_id":"quote_example"}'`
```

### GET `/v1/faxes/{fax_id}`

Read a fax job (with evidence once delivered)

Credential: an API key or a signed-in session. Scope: `fax:read`.

#### Parameters

GET /v1/faxes/{fax_id} parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `fax_id` | path | string | yes |  |

#### Responses

GET /v1/faxes/{fax_id} responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `404` | not_found: no such resource for this credential. A resource of another organization answers the same. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Response fields

GET /v1/faxes/{fax_id} response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount_micro` | integer | yes |  |
| `attempt_count` | integer | yes |  |
| `billable_pages` | integer | yes |  |
| `cancel_requested_at` | string or null | yes |  |
| `captured_micro` | integer | yes |  |
| `client_reference` | string or null | yes |  |
| `completed_at` | string or null | yes |  |
| `cover_pages` | integer | yes |  |
| `created_at` | string | yes |  |
| `currency` | string | yes |  |
| `delete_after_send` | boolean | no |  |
| `direction` | string | yes |  |
| `document_id` | string | yes |  |
| `document_sha256` | string | yes |  |
| `environment` | string (live, test) | yes |  |
| `evidence` | object or null | no | The carrier's evidence once delivered or partial; null before. |
| `failure_code` | string or null | yes |  |
| `from` | string or null | no |  |
| `id` | string | yes |  |
| `one_time` | object OneTimeJob or null | no |  |
| `one_time_order_id` | string or null | no | The one-time order this fax was paid by; null for a wallet fax. |
| `pages_sent` | integer | yes |  |
| `provider` | string | yes |  |
| `provider_ref` | string or null | yes |  |
| `quality` | string | no |  |
| `quote_id` | string or null | yes |  |
| `route` | string | yes |  |
| `sender_name` | string or null | no |  |
| `state` | string (queued, active, partial, failed, unknown, delivered) | yes |  |
| `to` | string | yes |  |
| `transmitted_sha256` | string or null | yes |  |
| `updated_at` | string | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes/FAX_ID \ -H "Authorization: Bearer ffa_test_..."`
```

### GET `/v1/faxes/{fax_id}/attempts`

Attempt history

Credential: an API key or a signed-in session. Scope: `fax:read`.

#### Parameters

GET /v1/faxes/{fax_id}/attempts parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `fax_id` | path | string | yes |  |

#### Responses

GET /v1/faxes/{fax_id}/attempts responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `404` | not_found: no such resource for this credential. A resource of another organization answers the same. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Response fields

GET /v1/faxes/{fax_id}/attempts response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | array of object Attempt | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes/FAX_ID/attempts \ -H "Authorization: Bearer ffa_test_..."`
```

### POST `/v1/faxes/{fax_id}/cancel`

Cancel if still possible (best effort once sending)

Credential: an API key or a signed-in session. Scope: `fax:cancel`.

#### Parameters

POST /v1/faxes/{fax_id}/cancel parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `fax_id` | path | string | yes |  |
| `Idempotency-Key` | header | string | no | Replay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay. |

#### Responses

POST /v1/faxes/{fax_id}/cancel responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `404` | not_found: no such resource for this credential. A resource of another organization answers the same. |
| `409` | The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Response fields

POST /v1/faxes/{fax_id}/cancel response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount_micro` | integer | yes |  |
| `attempt_count` | integer | yes |  |
| `billable_pages` | integer | yes |  |
| `cancel_requested_at` | string or null | yes |  |
| `captured_micro` | integer | yes |  |
| `client_reference` | string or null | yes |  |
| `completed_at` | string or null | yes |  |
| `cover_pages` | integer | yes |  |
| `created_at` | string | yes |  |
| `currency` | string | yes |  |
| `delete_after_send` | boolean | no |  |
| `direction` | string | yes |  |
| `document_id` | string | yes |  |
| `document_sha256` | string | yes |  |
| `environment` | string (live, test) | yes |  |
| `evidence` | object or null | no | The carrier's evidence once delivered or partial; null before. |
| `failure_code` | string or null | yes |  |
| `from` | string or null | no |  |
| `id` | string | yes |  |
| `one_time` | object OneTimeJob or null | no |  |
| `one_time_order_id` | string or null | no | The one-time order this fax was paid by; null for a wallet fax. |
| `pages_sent` | integer | yes |  |
| `provider` | string | yes |  |
| `provider_ref` | string or null | yes |  |
| `quality` | string | no |  |
| `quote_id` | string or null | yes |  |
| `route` | string | yes |  |
| `sender_name` | string or null | no |  |
| `state` | string (queued, active, partial, failed, unknown, delivered) | yes |  |
| `to` | string | yes |  |
| `transmitted_sha256` | string or null | yes |  |
| `updated_at` | string | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes/FAX_ID/cancel \ -X POST \ -H "Authorization: Bearer ffa_test_..."`
```

### GET `/v1/faxes/{fax_id}/events`

Ordered events of one fax

Credential: an API key or a signed-in session. Scope: `fax:read`.

#### Parameters

GET /v1/faxes/{fax_id}/events parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `fax_id` | path | string | yes |  |

#### Responses

GET /v1/faxes/{fax_id}/events responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `404` | not_found: no such resource for this credential. A resource of another organization answers the same. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Response fields

GET /v1/faxes/{fax_id}/events response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `items` | array of object Event | yes |  |
| `next_cursor` | string or null | no |  |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes/FAX_ID/events \ -H "Authorization: Bearer ffa_test_..."`
```

### GET `/v1/faxes/{fax_id}/receipt`

Delivery evidence as a receipt (JSON)

Credential: an API key or a signed-in session. Scope: `fax:read`.

#### Parameters

GET /v1/faxes/{fax_id}/receipt parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `fax_id` | path | string | yes |  |

#### Responses

GET /v1/faxes/{fax_id}/receipt responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `404` | not_found: no such resource for this credential. A resource of another organization answers the same. |
| `409` | The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Response fields

GET /v1/faxes/{fax_id}/receipt response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `amount_micro` | integer | yes |  |
| `attempts` | array of object Attempt | yes |  |
| `billable_pages` | integer | yes |  |
| `captured_micro` | integer | yes |  |
| `completed_at` | string or null | yes |  |
| `cover_pages` | integer | no | How many of the billable pages are the cover sheet: 0 or 1. |
| `created_at` | string | yes |  |
| `currency` | string | yes |  |
| `document_sha256` | string | yes |  |
| `evidence` | object or null | yes |  |
| `failure_code` | string or null | yes |  |
| `fax_job_id` | string | yes |  |
| `from` | string or null | no |  |
| `generated_at` | string | yes |  |
| `note` | string | yes |  |
| `one_time` | object OneTimeJob or null | no | Set on the receipt of a fax that a one-time order paid for. It names the order, its status, the card and the amount the card was authorized for. captured_micro in it is the amount charged, written once the payment provider confirmed the capture. For a card order read the money here. Null for a fax paid from the wallet. |
| `pages_sent` | integer | yes |  |
| `provider` | string | yes |  |
| `state` | string | yes |  |
| `to` | string | yes |  |
| `transmitted_sha256` | string or null | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes/FAX_ID/receipt \ -H "Authorization: Bearer ffa_test_..."`
```

### GET `/v1/faxes/{fax_id}/receipt.pdf`

The same receipt as a one-page PDF

Credential: an API key or a signed-in session. Scope: `fax:read`.

#### Parameters

GET /v1/faxes/{fax_id}/receipt.pdf parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `fax_id` | path | string | yes |  |

#### Responses

GET /v1/faxes/{fax_id}/receipt.pdf responses

| Status | Meaning |
| --- | --- |
| `200` | The receipt as a PDF |
| `401` | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
| `403` | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
| `404` | not_found: no such resource for this credential. A resource of another organization answers the same. |
| `409` | The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state. |
| `422` | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
| `429` | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |

#### Example

```
`curl https://api.faxforagents.com/v1/faxes/FAX_ID/receipt.pdf \ -H "Authorization: Bearer ffa_test_..."`
```
