> Docs index: https://convertextrato.com/llms.txt

# Jobs


A Job is one upload of one or more files. The API extracts each file in the background.

| Method   | Endpoint                                                          | Description                             | Permission   |
| -------- | ----------------------------------------------------------------- | --------------------------------------- | ------------ |
| `POST`   | [`/v1/jobs`](#create-a-job)                                       | Create a Job                            | `jobs:write` |
| `GET`    | [`/v1/jobs/{id}`](#get-a-job)                                     | Get a Job with its results              | `jobs:read`  |
| `GET`    | [`/v1/jobs/{id}/statement-files/{fileId}`](#get-a-statement-file) | Get one Statement File with its results | `jobs:read`  |
| `GET`    | [`/v1/jobs`](#list-jobs)                                          | List Jobs                               | `jobs:read`  |
| `DELETE` | [`/v1/jobs/{id}`](#delete-a-job)                                  | Delete a Job and its data               | `jobs:write` |

## The Job object

| Field       | Type   | Description                                       |
| ----------- | ------ | ------------------------------------------------- |
| `id`        | string | Job id.                                           |
| `status`    | string | `pending`, `processing`, `processed` or `failed`. |
| `createdAt` | string | ISO 8601 timestamp.                               |
| `files`     | array  | One entry per uploaded file.                      |

Each file:

| Field          | Type           | Description                                          |
| -------------- | -------------- | ---------------------------------------------------- |
| `id`           | string         | File id.                                             |
| `name`         | string         | Uploaded file name.                                  |
| `status`       | string         | `pending`, `processing`, `extracted` or `failed`.    |
| `pageCount`    | number         | Pages counted against the plan.                      |
| `mode`         | string         | `smart` or `simple`.                                 |
| `error`        | string or null | Why extraction failed, when `status` is `failed`.    |
| `statement`    | object         | Statement header. Only when `status` is `extracted`. |
| `transactions` | array          | Smart mode only, when `status` is `extracted`.       |
| `rows`         | array          | Simple mode only, when `status` is `extracted`.      |

### Statement

Every field is `null` when the statement does not show it.

| Field              | Type   | Description                         |
| ------------------ | ------ | ----------------------------------- |
| `bankName`         | string | Bank name.                          |
| `bankBranch`       | string | Branch, partly masked.              |
| `accountNumber`    | string | Account number, partly masked.      |
| `accountType`      | string | For example `Checking`.             |
| `customerName`     | string | Account holder, partly masked.      |
| `customerDocument` | string | Holder's tax id, partly masked.     |
| `customerType`     | string | `person` or `company`.              |
| `startDate`        | string | First day of the period, ISO 8601.  |
| `endDate`          | string | Last day of the period, ISO 8601.   |
| `invoiceDate`      | string | Statement date, ISO 8601.           |
| `openingBalance`   | number | Balance at the start of the period. |
| `closingBalance`   | number | Balance at the end of the period.   |
| `currency`         | string | ISO 4217 code, for example `USD`.   |

### Transaction (smart mode)

Every field is `null` when the statement does not show it.

| Field         | Type   | Description                                |
| ------------- | ------ | ------------------------------------------ |
| `date`        | string | ISO 8601 date.                             |
| `description` | string | Description as printed.                    |
| `amount`      | number | Negative for debits, positive for credits. |
| `balance`     | number | Balance after the transaction.             |
| `type`        | string | For example `Deposit` or `Withdrawal`.     |
| `category`    | string | Inferred category, for example `Salary`.   |
| `reference`   | string | Document or reference number.              |

### Rows (simple mode)

`rows` is the statement table as printed: an array of rows, each an array of cell strings. Simple mode does not interpret amounts or dates.

## Create a Job

```http
POST /v1/jobs
```

Uploads files and starts extraction. The body is `multipart/form-data`.

| Field   | Required | Description                                                                                  |
| ------- | -------- | -------------------------------------------------------------------------------------------- |
| `files` | Yes      | One or more files. Repeat the field for each file. PDF, JPEG, PNG or WebP, up to 10 MB each. |
| `mode`  | No       | `smart` (default) returns typed transactions. `simple` returns the table rows as printed.    |

Keep the whole request under 20 MB. The platform rejects larger requests before they reach the API, so the response has no JSON error body.

```bash
curl https://energized-butterfly-12.convex.site/v1/jobs \
  -H "Authorization: Bearer $CONVERT_API_KEY" \
  -F "files=@january.pdf" \
  -F "files=@february.pdf" \
  -F "mode=smart"
```

```js
const body = new FormData();
body.append(
  "files",
  new Blob([pdfBytes], { type: "application/pdf" }),
  "january.pdf",
);
body.append("mode", "smart");

const res = await fetch("https://energized-butterfly-12.convex.site/v1/jobs", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.CONVERT_API_KEY}` },
  body,
});
const job = await res.json();
```

Returns `202 Accepted` with the Job in `pending` status.

The API checks every file before it accepts the Job. If any file fails a check, nothing is stored and no pages are spent:

- `400` for a bad file type, size or page count.
- `409 DUPLICATE_FILE` when a file was already converted with this account. List Jobs to find it instead of uploading again.
- `402 QUOTA_EXCEEDED` when the Job's pages exceed the pages left this month.

## Get a Job

```http
GET /v1/jobs/{id}
```

Returns the Job with its results. Poll this endpoint every 5 seconds until `status` is `processed` or `failed`.

```bash
curl https://energized-butterfly-12.convex.site/v1/jobs/jn7craq7xc3qjq1knfzy5612f98gqnmh \
  -H "Authorization: Bearer $CONVERT_API_KEY"
```

```js
async function waitForJob(id) {
  for (;;) {
    const res = await fetch(`https://energized-butterfly-12.convex.site/v1/jobs/${id}`, {
      headers: { Authorization: `Bearer ${process.env.CONVERT_API_KEY}` },
    });
    const job = await res.json();
    if (job.status === "processed" || job.status === "failed") return job;
    await new Promise((r) => setTimeout(r, 5000));
  }
}
```

A `processed` Job can still contain `failed` files. Check each file's `status`.

Returns `404 JOB_NOT_FOUND` for an unknown id or a Job of another account.

## Get a Statement File

```http
GET /v1/jobs/{id}/statement-files/{fileId}
```

Returns one entry of the Job's `files`, in the same format. Use it when you store file ids, or to poll one file of a large Job. A file that is not `extracted` yet has no `statement`, `transactions` or `rows`.

```bash
curl https://energized-butterfly-12.convex.site/v1/jobs/jn7craq7xc3qjq1knfzy5612f98gqnmh/statement-files/jd718c20qj99x07hqmb6q48kn98gqamb \
  -H "Authorization: Bearer $CONVERT_API_KEY"
```

Returns `404 JOB_NOT_FOUND` when the Job does not exist for your account, and `404 STATEMENT_FILE_NOT_FOUND` when the file is not in that Job.

## List Jobs

```http
GET /v1/jobs
```

Lists your Jobs, newest first, without their results.

| Query parameter | Default | Description                          |
| --------------- | ------- | ------------------------------------ |
| `limit`         | `20`    | Jobs per page, from 1 to 100.        |
| `cursor`        |         | `nextCursor` from the previous page. |

```bash
curl "https://energized-butterfly-12.convex.site/v1/jobs?limit=10" \
  -H "Authorization: Bearer $CONVERT_API_KEY"
```

```json
{
  "jobs": [
    {
      "id": "jn7craq7xc3qjq1knfzy5612f98gqnmh",
      "status": "processed",
      "createdAt": "2026-10-21T12:51:02.816Z",
      "fileCount": 1
    }
  ],
  "nextCursor": "eyJ..."
}
```

`nextCursor` is `null` on the last page.

## Delete a Job

```http
DELETE /v1/jobs/{id}
```

Deletes the uploaded files and the extracted data. This cannot be undone. The Job's pages still count against this month's plan.

```bash
curl -X DELETE https://energized-butterfly-12.convex.site/v1/jobs/jn7craq7xc3qjq1knfzy5612f98gqnmh \
  -H "Authorization: Bearer $CONVERT_API_KEY"
```

Returns `204 No Content`. A Job still `pending` or `processing` returns `409 JOB_IN_PROGRESS`; wait for it to finish first.
