# Jobs — API reference

Asking for work and finding out how it went.

## GET /v1/jobs

List jobs

Parameters

| Name | In | Requirement | Description |
| --- | --- | --- | --- |
| `limit` | query | Optional | Page size. |
| `starting_after` | query | Optional | Cursor from the previous page’s `next_cursor`. |
| `state` | query | Optional | Filter by job state. |
| `project_id` | query | Optional | Filter by project. |

Responses

| Status | Description |
| --- | --- |
| `200` | A page of jobs, newest first. |
| `400` | Codes: invalid_request, unsupported_file, file_too_large, corrupted_file, encrypted_file, unsupported_operation. |
| `401` | Codes: missing_credentials, invalid_api_key, expired_api_key, revoked_api_key, api_key_in_query. |
| `403` | Codes: insufficient_scope, ip_not_allowed, environment_mismatch, organization_suspended, forbidden, key_restricted. |
| `429` | Rate limited or out of credits. Retry after the interval in `Retry-After`. Codes: key_spend_limit, rate_limited, concurrency_limit, credits_exhausted. |
| `500` | Codes: internal_error. |
| `503` | Codes: processing_unavailable, dependency_unavailable. |
| `504` | Codes: timeout. |

## POST /v1/jobs

Accepts the work and returns immediately with `202`. Credits are reserved atomically at this point — a job that would exceed the balance is refused here rather than discovered on an invoice. Supply exactly one of `operation` or `preset`.

Parameters

| Name | In | Requirement | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header | Required | Client-generated key, scoped to (organization, endpoint) and retained 24 hours. A replay with the same body returns the stored response without re-executing or re-billing; a replay with a different body returns 409. |

Request body

| Field | Type | Requirement | Description |
| --- | --- | --- | --- |
| `file_id` | `string` | Required | The file to process. |
| `operation` | `string` | Optional |  |
| `preset` | `ai-ready` · `document` · `image` · `avatar` · `inspect` | Optional |  |
| `options` | `object` | Optional | Operation-specific parameters. |

Responses

| Status | Description |
| --- | --- |
| `202` | Accepted. The job is queued; subscribe to a webhook or poll `GET /v1/jobs/{job_id}`. |
| `400` | Codes: invalid_request, unsupported_file, file_too_large, corrupted_file, encrypted_file, unsupported_operation. |
| `401` | Codes: missing_credentials, invalid_api_key, expired_api_key, revoked_api_key, api_key_in_query. |
| `403` | Codes: insufficient_scope, ip_not_allowed, environment_mismatch, organization_suspended, forbidden, key_restricted. |
| `404` | Not found. A resource belonging to another organization returns 404, never 403 — a 403 would confirm it exists. Codes: not_found. |
| `409` | Codes: idempotency_conflict, upload_session_consumed, conflict. |
| `422` | Codes: malware_detected, policy_violation, validation_failed, output_validation_failed, unsupported_input. |
| `429` | Rate limited or out of credits. Retry after the interval in `Retry-After`. Codes: key_spend_limit, rate_limited, concurrency_limit, credits_exhausted. |
| `500` | Codes: internal_error. |
| `503` | Codes: processing_unavailable, dependency_unavailable. |
| `504` | Codes: timeout. |

## GET /v1/jobs/{job_id}

Retrieve a job

Parameters

| Name | In | Requirement | Description |
| --- | --- | --- | --- |
| `job_id` | path | Required | Job identifier. |

Responses

| Status | Description |
| --- | --- |
| `200` | The job. |
| `401` | Codes: missing_credentials, invalid_api_key, expired_api_key, revoked_api_key, api_key_in_query. |
| `403` | Codes: insufficient_scope, ip_not_allowed, environment_mismatch, organization_suspended, forbidden, key_restricted. |
| `404` | Not found. A resource belonging to another organization returns 404, never 403 — a 403 would confirm it exists. Codes: not_found. |
| `429` | Rate limited or out of credits. Retry after the interval in `Retry-After`. Codes: key_spend_limit, rate_limited, concurrency_limit, credits_exhausted. |
| `500` | Codes: internal_error. |
| `503` | Codes: processing_unavailable, dependency_unavailable. |
| `504` | Codes: timeout. |

## POST /v1/batches

One operation across up to 100 files, accepted atomically: every member is admitted and priced in a single transaction, or the batch does not exist. Members are then ordinary jobs — same queues, same retries, same webhooks, same settlement — and a member that fails is a per-item error, charged nothing, while the rest proceed.

Parameters

| Name | In | Requirement | Description |
| --- | --- | --- | --- |
| `Idempotency-Key` | header | Required | Client-generated key, scoped to (organization, endpoint) and retained 24 hours. A replay with the same body returns the stored response without re-executing or re-billing; a replay with a different body returns 409. |

Request body

| Field | Type | Requirement | Description |
| --- | --- | --- | --- |
| `file_ids` | array of `string` | Required |  |
| `operation` | `string` | Optional |  |
| `preset` | `ai-ready` · `document` · `image` · `avatar` · `inspect` | Optional |  |
| `options` | `object` | Optional | Operation-specific parameters, applied to every member. |

Responses

| Status | Description |
| --- | --- |
| `202` | Accepted. Every member is queued; poll `GET /v1/batches/{batch_id}` or subscribe to job webhooks. |
| `400` | Codes: invalid_request, unsupported_file, file_too_large, corrupted_file, encrypted_file, unsupported_operation. |
| `401` | Codes: missing_credentials, invalid_api_key, expired_api_key, revoked_api_key, api_key_in_query. |
| `403` | Codes: insufficient_scope, ip_not_allowed, environment_mismatch, organization_suspended, forbidden, key_restricted. |
| `404` | Not found. A resource belonging to another organization returns 404, never 403 — a 403 would confirm it exists. Codes: not_found. |
| `422` | Codes: malware_detected, policy_violation, validation_failed, output_validation_failed, unsupported_input. |
| `429` | Rate limited or out of credits. Retry after the interval in `Retry-After`. Codes: key_spend_limit, rate_limited, concurrency_limit, credits_exhausted. |
| `500` | Codes: internal_error. |
| `503` | Codes: processing_unavailable, dependency_unavailable. |
| `504` | Codes: timeout. |

## GET /v1/batches/{batch_id}

Aggregated state counts, credits estimated and charged, and the per-item outcomes. `state` is `completed` once every member is terminal — individual failures are items, not a batch failure.

Parameters

| Name | In | Requirement | Description |
| --- | --- | --- | --- |
| `batch_id` | path | Required | Batch identifier. |

Responses

| Status | Description |
| --- | --- |
| `200` | The batch and its members. |
| `401` | Codes: missing_credentials, invalid_api_key, expired_api_key, revoked_api_key, api_key_in_query. |
| `403` | Codes: insufficient_scope, ip_not_allowed, environment_mismatch, organization_suspended, forbidden, key_restricted. |
| `404` | Not found. A resource belonging to another organization returns 404, never 403 — a 403 would confirm it exists. Codes: not_found. |
| `429` | Rate limited or out of credits. Retry after the interval in `Retry-After`. Codes: key_spend_limit, rate_limited, concurrency_limit, credits_exhausted. |
| `500` | Codes: internal_error. |
| `503` | Codes: processing_unavailable, dependency_unavailable. |
| `504` | Codes: timeout. |

## POST /v1/jobs/{job_id}/cancel

Only queued work can be reclaimed. A job the worker already claimed keeps running — compute cannot be un-spent — and answers 409 with its state. Subscribers hear `job.cancelled`.

Parameters

| Name | In | Requirement | Description |
| --- | --- | --- | --- |
| `job_id` | path | Required | Job identifier. |
| `Idempotency-Key` | header | Required | Client-generated key, scoped to (organization, endpoint) and retained 24 hours. A replay with the same body returns the stored response without re-executing or re-billing; a replay with a different body returns 409. |

Responses

| Status | Description |
| --- | --- |
| `200` | Cancelled. |
| `401` | Codes: missing_credentials, invalid_api_key, expired_api_key, revoked_api_key, api_key_in_query. |
| `403` | Codes: insufficient_scope, ip_not_allowed, environment_mismatch, organization_suspended, forbidden, key_restricted. |
| `404` | Not found. A resource belonging to another organization returns 404, never 403 — a 403 would confirm it exists. Codes: not_found. |
| `409` | Codes: idempotency_conflict, upload_session_consumed, conflict. |
| `429` | Rate limited or out of credits. Retry after the interval in `Retry-After`. Codes: key_spend_limit, rate_limited, concurrency_limit, credits_exhausted. |
| `500` | Codes: internal_error. |
| `503` | Codes: processing_unavailable, dependency_unavailable. |
| `504` | Codes: timeout. |
