# Documents API reference

> The document operations of the Fax for Agents API. Upload a PDF or an image, or render text, Markdown or HTML, then read the hash, the pages and the warnings.

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

Updated Nov 13, 2026

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

- A document is immutable once created, and its sha256 identifies exactly what will be sent.
- Completing an upload checks the size and, when one is sent, the sha256 of the bytes.
- The bytes of an upload go to a signed upload URL with no API key.

## Operations

- `GET /v1/documents` List documents, newest first
- `POST /v1/documents` Create an immutable document from an upload or from text
- `DELETE /v1/documents/{document_id}` Delete a document (its bytes and previews)
- `GET /v1/documents/{document_id}` Read a document's hash, pages and validation state
- `GET /v1/documents/{document_id}/content` The PDF bytes behind a signed URL (no API key)
- `GET /v1/documents/{document_id}/download` A short-lived signed download URL
- `GET /v1/documents/{document_id}/pages` Rendered pages with signed preview URLs
- `GET /v1/documents/{document_id}/pages/{page_number}/content` A page PNG behind a signed URL (no API key)
- `GET /v1/documents/{document_id}/validation` Validation warnings
- `POST /v1/uploads` Create a bounded upload session
- `DELETE /v1/uploads/{upload_id}` Cancel an incomplete upload
- `POST /v1/uploads/{upload_id}/complete` Finalize an upload (size and hash checked)
- `PUT /v1/uploads/{upload_id}/content` Send the bytes to a signed upload URL (no API key)

## What is a document in this API?

The exact thing a fax will carry. `POST /v1/documents` creates it from an upload or from text, and the summary of the operation calls it immutable. The answer has a `sha256`, a `page_count`, the `source` and a `validation` object. Nothing changes those afterwards. A quote and an approval both name the hash, so a person who approves a send approves these bytes.

## How does an upload session work?

Four calls, and only three of them take your key.

`POST /v1/uploads` opens a bounded session. The body declares the `filename`, the `size_bytes` and the `content_type`, which is `application/pdf`, `image/png`, `image/jpeg` or `image/tiff`. The answer has the id and a signed `upload_url` with its expiry.

`PUT /v1/uploads/{upload_id}/content` is that signed URL. It takes the bytes and no API key. The signature in the address is the credential.

`POST /v1/uploads/{upload_id}/complete` closes the session. The size is checked against what was declared. The body can carry a `sha256`, and the call fails when the bytes differ. Send an empty JSON object when you have no hash.

`POST /v1/documents` with the `upload_id` then makes the document. `DELETE /v1/uploads/{upload_id}` cancels a session that was not completed.

## Can a document be made without a file?

Yes. The same create call takes `text`, `markdown` or `html` in place of `upload_id`, and exactly one of the four. Text is rendered to a letter PDF. Markdown renders headings, lists, emphasis and code. HTML is rendered by a headless browser with scripts off and no network, so inline CSS and `data:` images print and an external resource does not. A `filename` is optional.

The [send guide](https://faxforagents.com/docs/send-fax/#documents) has the fonts, the page size and what happens to an image.

## How do I check what was made?

`GET /v1/documents/{document_id}` returns the hash, the pages and the validation state. `GET /v1/documents/{document_id}/validation` returns the warnings alone, such as a blocked resource in HTML or an image that was rotated. `GET /v1/documents/{document_id}/pages` returns the rendered pages with signed preview URLs, so a person can look at each page before a send.

## How are the bytes downloaded?

Never through a public address. `GET /v1/documents/{document_id}/download` answers with a short-lived signed URL. The content routes behind such URLs, for the PDF and for one page as PNG, take `exp` and `sig` in the query and no key. When the link has expired, ask for a new one.

## What does deleting do?

`DELETE /v1/documents/{document_id}` removes the bytes and the previews. A fax that already used the document keeps its record of the hash and the page count. `GET /v1/documents` lists documents newest first with `cursor` and `limit`.

## Which scopes and limits apply?

Reads need `document:read`. Creating, deleting and the upload session need `document:write`. The signed routes need neither.

A file or a body over the size limit answers 413. A rendering service that is down answers 503 on the create call, and that call can be repeated. The page and size limits with their values are on the [limits page](https://faxforagents.com/docs/limits/).

## Each operation in detail

### GET `/v1/documents`

List documents, newest first

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

#### Parameters

GET /v1/documents parameters

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

#### Responses

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

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

#### Example

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

### POST `/v1/documents`

Create an immutable document from an upload or from text

Credential: an API key or a signed-in session. Scope: `document:write`.

#### Parameters

POST /v1/documents 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

Send exactly one of text, markdown, html or upload_id.

POST /v1/documents request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `filename` | string or null | no |  |
| `html` | string or null | no | HTML rendered to PDF by a headless browser with scripts off and no network: inline CSS and data: images print, external resources are counted as blocked. US letter unless @page says otherwise. At most 2 MiB. |
| `markdown` | string or null | no | Markdown (headings, lists, emphasis, code) rendered to PDF. |
| `text` | string or null | no | Plain text rendered to a letter PDF. |
| `upload_id` | string or null | no |  |

#### Responses

POST /v1/documents 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. |
| `413` | The body is larger than the limit of this call: payload_too_large. |
| `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. |
| `503` | Something this call depends on is not available or not set up here, for example billing_unavailable or provider_unavailable. error.retryable says whether to send the same call again. |

#### Response fields

POST /v1/documents response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `content_type` | string | yes |  |
| `created_at` | string | yes |  |
| `deleted_at` | string or null | yes |  |
| `environment` | string (live, test) | yes |  |
| `expires_at` | string or null | yes |  |
| `filename` | string | yes |  |
| `id` | string | yes |  |
| `page_count` | integer | yes |  |
| `render_reason` | string or null | yes |  |
| `render_status` | string | yes |  |
| `sha256` | string | yes |  |
| `size_bytes` | integer | yes |  |
| `source` | string | yes |  |
| `status` | string | yes |  |
| `upload_id` | string or null | yes |  |
| `validation` | object | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/documents \ -X POST \ -H "Authorization: Bearer ffa_test_..." \ -H "Content-Type: application/json" \ -d '{"text":"Hello from Fax for Agents"}'`
```

### DELETE `/v1/documents/{document_id}`

Delete a document (its bytes and previews)

Credential: an API key or a signed-in session. Scope: `document:write`.

#### Parameters

DELETE /v1/documents/{document_id} parameters

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

DELETE /v1/documents/{document_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. |
| `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

DELETE /v1/documents/{document_id} response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `deleted` | boolean | no |  |
| `id` | string | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/documents/DOCUMENT_ID \ -X DELETE \ -H "Authorization: Bearer ffa_test_..."`
```

### GET `/v1/documents/{document_id}`

Read a document's hash, pages and validation state

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

#### Parameters

GET /v1/documents/{document_id} parameters

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

#### Responses

GET /v1/documents/{document_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/documents/{document_id} response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `content_type` | string | yes |  |
| `created_at` | string | yes |  |
| `deleted_at` | string or null | yes |  |
| `environment` | string (live, test) | yes |  |
| `expires_at` | string or null | yes |  |
| `filename` | string | yes |  |
| `id` | string | yes |  |
| `page_count` | integer | yes |  |
| `render_reason` | string or null | yes |  |
| `render_status` | string | yes |  |
| `sha256` | string | yes |  |
| `size_bytes` | integer | yes |  |
| `source` | string | yes |  |
| `status` | string | yes |  |
| `upload_id` | string or null | yes |  |
| `validation` | object | yes |  |

#### Example

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

### GET `/v1/documents/{document_id}/content`

The PDF bytes behind a signed URL (no API key)

Credential: the signed URL as it was given.

#### Parameters

GET /v1/documents/{document_id}/content parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `document_id` | path | string | yes |  |
| `exp` | query | string or null | no |  |
| `sig` | query | string or null | no |  |

#### Responses

GET /v1/documents/{document_id}/content responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `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. |

#### Example

```
`curl https://api.faxforagents.com/v1/documents/DOCUMENT_ID/content`
```

### GET `/v1/documents/{document_id}/download`

A short-lived signed download URL

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

#### Parameters

GET /v1/documents/{document_id}/download parameters

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

#### Responses

GET /v1/documents/{document_id}/download 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/documents/{document_id}/download response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `content_type` | string | yes |  |
| `expires_at` | string | yes |  |
| `url` | string | yes |  |

#### Example

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

### GET `/v1/documents/{document_id}/pages`

Rendered pages with signed preview URLs

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

#### Parameters

GET /v1/documents/{document_id}/pages parameters

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

#### Responses

GET /v1/documents/{document_id}/pages 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/documents/{document_id}/pages response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `document_id` | string | yes |  |
| `items` | array of object PagePreview | yes |  |
| `page_count` | integer | yes |  |
| `render_reason` | string or null | yes |  |
| `render_status` | string | yes |  |

#### Example

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

### GET `/v1/documents/{document_id}/pages/{page_number}/content`

A page PNG behind a signed URL (no API key)

Credential: the signed URL as it was given.

#### Parameters

GET /v1/documents/{document_id}/pages/{page_number}/content parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `document_id` | path | string | yes |  |
| `page_number` | path | integer | yes |  |
| `exp` | query | string or null | no |  |
| `sig` | query | string or null | no |  |

#### Responses

GET /v1/documents/{document_id}/pages/{page_number}/content responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `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. |

#### Example

```
`curl https://api.faxforagents.com/v1/documents/DOCUMENT_ID/pages/PAGE_NUMBER/content`
```

### GET `/v1/documents/{document_id}/validation`

Validation warnings

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

#### Parameters

GET /v1/documents/{document_id}/validation parameters

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

#### Responses

GET /v1/documents/{document_id}/validation 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/documents/{document_id}/validation response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes |  |
| `page_count` | integer | yes |  |
| `render_reason` | string or null | yes |  |
| `render_status` | string | yes |  |
| `status` | string | yes |  |
| `warnings` | array of object | yes |  |

#### Example

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

### POST `/v1/uploads`

Create a bounded upload session

Credential: an API key or a signed-in session. Scope: `document:write`.

#### Parameters

POST /v1/uploads 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/uploads request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `content_type` | string | no | application/pdf, image/png, image/jpeg or image/tiff. An image becomes 8-bit gray letter pages, since fax is monochrome. A frame wider than it is tall is rotated. A frame above 300 dpi letter is downsampled. Every TIFF frame is a page. PNG and JPEG take the first frame. The bytes must be the declared type. |
| `filename` | string | yes |  |
| `size_bytes` | integer | yes |  |

#### Responses

POST /v1/uploads 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. |
| `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. |
| `413` | The body is larger than the limit of this call: payload_too_large. |
| `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/uploads response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `completed_at` | string or null | yes |  |
| `content_type` | string | yes |  |
| `created_at` | string | yes |  |
| `declared_size` | integer | yes |  |
| `expires_at` | string | yes |  |
| `filename` | string | yes |  |
| `id` | string | yes |  |
| `received_size` | integer | yes |  |
| `sha256` | string or null | yes |  |
| `status` | string | yes |  |
| `upload_method` | string or null | no |  |
| `upload_url` | string or null | no |  |
| `upload_url_expires_at` | string or null | no |  |

#### Example

```
`curl https://api.faxforagents.com/v1/uploads \ -X POST \ -H "Authorization: Bearer ffa_test_..." \ -H "Content-Type: application/json" \ -d '{"filename":"contract.pdf","size_bytes":1}'`
```

### DELETE `/v1/uploads/{upload_id}`

Cancel an incomplete upload

Credential: an API key or a signed-in session. Scope: `document:write`.

#### Parameters

DELETE /v1/uploads/{upload_id} parameters

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

DELETE /v1/uploads/{upload_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. |
| `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

DELETE /v1/uploads/{upload_id} response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `deleted` | boolean | no |  |
| `id` | string | yes |  |

#### Example

```
`curl https://api.faxforagents.com/v1/uploads/UPLOAD_ID \ -X DELETE \ -H "Authorization: Bearer ffa_test_..."`
```

### POST `/v1/uploads/{upload_id}/complete`

Finalize an upload (size and hash checked)

Credential: an API key or a signed-in session. Scope: `document:write`.

#### Parameters

POST /v1/uploads/{upload_id}/complete parameters

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

POST /v1/uploads/{upload_id}/complete request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `sha256` | string or null | no | Optional: fail if the bytes differ. |

#### Responses

POST /v1/uploads/{upload_id}/complete 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/uploads/{upload_id}/complete response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `completed_at` | string or null | yes |  |
| `content_type` | string | yes |  |
| `created_at` | string | yes |  |
| `declared_size` | integer | yes |  |
| `expires_at` | string | yes |  |
| `filename` | string | yes |  |
| `id` | string | yes |  |
| `received_size` | integer | yes |  |
| `sha256` | string or null | yes |  |
| `status` | string | yes |  |
| `upload_method` | string or null | no |  |
| `upload_url` | string or null | no |  |
| `upload_url_expires_at` | string or null | no |  |

#### Example

```
`curl https://api.faxforagents.com/v1/uploads/UPLOAD_ID/complete \ -X POST \ -H "Authorization: Bearer ffa_test_..."`
```

### PUT `/v1/uploads/{upload_id}/content`

Send the bytes to a signed upload URL (no API key)

Credential: the signed URL as it was given.

#### Parameters

PUT /v1/uploads/{upload_id}/content parameters

| Name | In | Type | Required | Notes |
| --- | --- | --- | --- | --- |
| `upload_id` | path | string | yes |  |
| `exp` | query | string or null | no |  |
| `sig` | query | string or null | no |  |
| `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

PUT /v1/uploads/{upload_id}/content responses

| Status | Meaning |
| --- | --- |
| `200` | Successful Response |
| `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. |
| `413` | The body is larger than the limit of this call: payload_too_large. |
| `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

PUT /v1/uploads/{upload_id}/content response fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `completed_at` | string or null | yes |  |
| `content_type` | string | yes |  |
| `created_at` | string | yes |  |
| `declared_size` | integer | yes |  |
| `expires_at` | string | yes |  |
| `filename` | string | yes |  |
| `id` | string | yes |  |
| `received_size` | integer | yes |  |
| `sha256` | string or null | yes |  |
| `status` | string | yes |  |
| `upload_method` | string or null | no |  |
| `upload_url` | string or null | no |  |
| `upload_url_expires_at` | string or null | no |  |

#### Example

```
`curl https://api.faxforagents.com/v1/uploads/UPLOAD_ID/content \ -X PUT`
```
