# Approvals API reference

> The approval operations of the Fax for Agents API. An agent asks a person to approve one quoted send, the person decides, and an unused approval can be revoked.

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

Updated Nov 13, 2026

7 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/).

- Creating an approval request grants nothing until a person approves it.
- Approving needs approval:decide from a fresh session or an independent API credential.
- Only an approval that nobody has used yet can be revoked.

## Operations

- `GET /v1/approval-requests` Approval requests, pending first
- `POST /v1/approval-requests` Ask a human to approve a quoted send, or ask for help (grants nothing)
- `GET /v1/approval-requests/{approval_id}` Read one request
- `POST /v1/approval-requests/{approval_id}/approve` Approve the bound quote (approval:decide, fresh session or independent API credential)
- `POST /v1/approval-requests/{approval_id}/reject` Reject with a reason
- `POST /v1/approvals/{approval_id}/revoke` Revoke an approval nobody has used yet
- `GET /v1/integration-reviews/{org_id}/{environment}/{operation_id}/{binding_id}` Get Integration Review

## What is an approval request?

An approval request asks a person to say yes to one send. The agent names a quote, and the server copies what the quote fixes into the request. That is the document and its hash, the destination, the sender and the amount. The answer returns that copy under `bound`. The request expires with the quote.

The summary of the create operation ends with "grants nothing". A pending request cannot send a fax. Only an approved one can, once, for the quote it was made from.

## How does an agent ask?

`POST /v1/approval-requests` with a `quote_id` opens a request of kind send. It needs `approval:request`, which agent keys carry. The answer has the id, the status, the kind and who asked.

The same operation opens a request of kind help. The body then carries a `message` and can name a `fax_job_id`. The schema says the message is shown as data: a person reads it, and the server never acts on it.

## Who can approve or reject?

`POST /v1/approval-requests/{approval_id}/approve` and `/reject` need `approval:decide`. The summary of the approve operation names the two ways to hold it. One is a fresh session, which means a person who signed in or proved it again a short time ago. The other is an independent API credential, which means a different API key from the one that asked. An agent key is never issued the scope.

Both take a `reason`. Rejecting records why, and the requester can read it. The [approvals guide](https://faxforagents.com/docs/approvals/) has the codes a later send can be refused with.

## How does the agent learn the decision?

`GET /v1/approval-requests/{approval_id}` returns the request with its status, `decided_at`, `decided_by` and the reason. An approved request is what `POST /v1/faxes` takes as `approval_id`. After the send, `consumed_by_job_id` names the fax that used it.

`GET /v1/approval-requests` lists requests with the pending ones first. It filters by `status` and pages with `cursor` and `limit`. Both reads need `approval:read`.

## Can an approval be taken back?

`POST /v1/approvals/{approval_id}/revoke` withdraws an approval that nobody has used yet. It needs `approval:decide`, like the decision itself. After a revoke the agent has to quote and ask again. An approval a fax has consumed cannot be revoked. Cancel the fax in the [faxes group](https://faxforagents.com/docs/api/reference/faxes/) when it has not ended.

## What is the integration review read?

`GET /v1/integration-reviews/{org_id}/{environment}/{operation_id}/{binding_id}` returns the approval request that one review binding names, in the same shape as the other reads. It takes a signed-in session only, with `approval:read`. The organization and the environment in the path have to be the ones the session is looking at. An API key is refused.

## What does a send need besides an approval?

Nothing more from this group. A live send needs either an approved request or a [delegated policy](https://faxforagents.com/docs/policies/) that allows it. Test keys send in the sandbox without either. The approval is checked again at the send. A changed document, destination, sender or amount no longer matches what was approved, and the send is refused.

## Each operation in detail

### GET `/v1/approval-requests`

Approval requests, pending first

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

#### Parameters

GET /v1/approval-requests parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `status` | query | string or null | no |  |
| `cursor` | query | string or null | no |  |
| `limit` | query | integer | no | Default `50` . |

#### Responses

GET /v1/approval-requests 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/approval-requests response fields

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

#### Example

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

### POST `/v1/approval-requests`

Ask a human to approve a quoted send, or ask for help (grants nothing)

Credential: an API key or a signed-in session. Scope: `approval:request`.

#### Parameters

POST /v1/approval-requests 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

POST /v1/approval-requests request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `fax_job_id` | string or null | no |  |
| `kind` | string (send, help) | no |  |
| `message` | string or null | no | What the person should know (kind help). Shown as data. |
| `quote_id` | string or null | no | The quote to bind (kind send). |

#### Responses

POST /v1/approval-requests 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. |
| `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/approval-requests response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bound` | object | yes |  |
| `consumed_by_job_id` | string or null | yes |  |
| `created_at` | string | yes |  |
| `decided_at` | string or null | yes |  |
| `decided_by` | string or null | yes |  |
| `expires_at` | string or null | yes |  |
| `id` | string | yes |  |
| `kind` | string (send, help) | yes |  |
| `message` | string or null | yes |  |
| `notified` | boolean or null | no | On creation: whether the organization was mailed. false when the request repeats a pending one or the organization's daily cap of request mails is reached; the request is still shown in the dashboard. |
| `quote_id` | string or null | yes |  |
| `reason` | string or null | yes |  |
| `requested_by` | string | yes |  |
| `status` | string (pending, approved, rejected, revoked, expired) | yes |  |

#### Example

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

### GET `/v1/approval-requests/{approval_id}`

Read one request

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

#### Parameters

GET /v1/approval-requests/{approval_id} parameters

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

#### Responses

GET /v1/approval-requests/{approval_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/approval-requests/{approval_id} response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bound` | object | yes |  |
| `consumed_by_job_id` | string or null | yes |  |
| `created_at` | string | yes |  |
| `decided_at` | string or null | yes |  |
| `decided_by` | string or null | yes |  |
| `expires_at` | string or null | yes |  |
| `id` | string | yes |  |
| `kind` | string (send, help) | yes |  |
| `message` | string or null | yes |  |
| `notified` | boolean or null | no | On creation: whether the organization was mailed. false when the request repeats a pending one or the organization's daily cap of request mails is reached; the request is still shown in the dashboard. |
| `quote_id` | string or null | yes |  |
| `reason` | string or null | yes |  |
| `requested_by` | string | yes |  |
| `status` | string (pending, approved, rejected, revoked, expired) | yes |  |

#### Example

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

### POST `/v1/approval-requests/{approval_id}/approve`

Approve the bound quote (approval:decide, fresh session or independent API credential)

Credential: an API key or a signed-in session. Scope: `approval:decide`.

#### Parameters

POST /v1/approval-requests/{approval_id}/approve parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `approval_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. |

#### Request body application/json, required

POST /v1/approval-requests/{approval_id}/approve request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string or null | no |  |

#### Responses

POST /v1/approval-requests/{approval_id}/approve 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/approval-requests/{approval_id}/approve response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bound` | object | yes |  |
| `consumed_by_job_id` | string or null | yes |  |
| `created_at` | string | yes |  |
| `decided_at` | string or null | yes |  |
| `decided_by` | string or null | yes |  |
| `expires_at` | string or null | yes |  |
| `id` | string | yes |  |
| `kind` | string (send, help) | yes |  |
| `message` | string or null | yes |  |
| `notified` | boolean or null | no | On creation: whether the organization was mailed. false when the request repeats a pending one or the organization's daily cap of request mails is reached; the request is still shown in the dashboard. |
| `quote_id` | string or null | yes |  |
| `reason` | string or null | yes |  |
| `requested_by` | string | yes |  |
| `status` | string (pending, approved, rejected, revoked, expired) | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/approval-requests/APPROVAL_ID/approve \ -X POST \ -H "Authorization: Bearer ffa_test_..." \ -H "Content-Type: application/json" \ -d '{}'`
```

### POST `/v1/approval-requests/{approval_id}/reject`

Reject with a reason

Credential: an API key or a signed-in session. Scope: `approval:decide`.

#### Parameters

POST /v1/approval-requests/{approval_id}/reject parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `approval_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. |

#### Request body application/json, required

POST /v1/approval-requests/{approval_id}/reject request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string or null | no |  |

#### Responses

POST /v1/approval-requests/{approval_id}/reject 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/approval-requests/{approval_id}/reject response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bound` | object | yes |  |
| `consumed_by_job_id` | string or null | yes |  |
| `created_at` | string | yes |  |
| `decided_at` | string or null | yes |  |
| `decided_by` | string or null | yes |  |
| `expires_at` | string or null | yes |  |
| `id` | string | yes |  |
| `kind` | string (send, help) | yes |  |
| `message` | string or null | yes |  |
| `notified` | boolean or null | no | On creation: whether the organization was mailed. false when the request repeats a pending one or the organization's daily cap of request mails is reached; the request is still shown in the dashboard. |
| `quote_id` | string or null | yes |  |
| `reason` | string or null | yes |  |
| `requested_by` | string | yes |  |
| `status` | string (pending, approved, rejected, revoked, expired) | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/approval-requests/APPROVAL_ID/reject \ -X POST \ -H "Authorization: Bearer ffa_test_..." \ -H "Content-Type: application/json" \ -d '{}'`
```

### POST `/v1/approvals/{approval_id}/revoke`

Revoke an approval nobody has used yet

Credential: an API key or a signed-in session. Scope: `approval:decide`.

#### Parameters

POST /v1/approvals/{approval_id}/revoke parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `approval_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. |

#### Request body application/json, required

POST /v1/approvals/{approval_id}/revoke request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string or null | no |  |

#### Responses

POST /v1/approvals/{approval_id}/revoke 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/approvals/{approval_id}/revoke response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bound` | object | yes |  |
| `consumed_by_job_id` | string or null | yes |  |
| `created_at` | string | yes |  |
| `decided_at` | string or null | yes |  |
| `decided_by` | string or null | yes |  |
| `expires_at` | string or null | yes |  |
| `id` | string | yes |  |
| `kind` | string (send, help) | yes |  |
| `message` | string or null | yes |  |
| `notified` | boolean or null | no | On creation: whether the organization was mailed. false when the request repeats a pending one or the organization's daily cap of request mails is reached; the request is still shown in the dashboard. |
| `quote_id` | string or null | yes |  |
| `reason` | string or null | yes |  |
| `requested_by` | string | yes |  |
| `status` | string (pending, approved, rejected, revoked, expired) | yes |  |

#### Example

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

### GET `/v1/integration-reviews/{org_id}/{environment}/{operation_id}/{binding_id}`

Get Integration Review

Credential: a signed-in session. Scope: `approval:read`.

#### Parameters

GET /v1/integration-reviews/{org_id}/{environment}/{operation_id}/{binding_id} parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `org_id` | path | string | yes |  |
| `environment` | path | string | yes |  |
| `operation_id` | path | string | yes |  |
| `binding_id` | path | string | yes |  |

#### Responses

GET /v1/integration-reviews/{org_id}/{environment}/{operation_id}/{binding_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/integration-reviews/{org_id}/{environment}/{operation_id}/{binding_id} response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `bound` | object | yes |  |
| `consumed_by_job_id` | string or null | yes |  |
| `created_at` | string | yes |  |
| `decided_at` | string or null | yes |  |
| `decided_by` | string or null | yes |  |
| `expires_at` | string or null | yes |  |
| `id` | string | yes |  |
| `kind` | string (send, help) | yes |  |
| `message` | string or null | yes |  |
| `notified` | boolean or null | no | On creation: whether the organization was mailed. false when the request repeats a pending one or the organization's daily cap of request mails is reached; the request is still shown in the dashboard. |
| `quote_id` | string or null | yes |  |
| `reason` | string or null | yes |  |
| `requested_by` | string | yes |  |
| `status` | string (pending, approved, rejected, revoked, expired) | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/integration-reviews/ORG_ID/ENVIRONMENT/OPERATION_ID/BINDING_ID`
```
