# Jobs and bounded waiting

Read job status, credits and outputs, and wait for a job without polling.

Source: https://scifig.ai/docs/api/jobs

## Read a job

`GET /jobs/{id}` (scope `jobs:read`) returns the same job object as the create request. Jobs of other accounts and unknown IDs both return `404 NOT_FOUND`. `GET /jobs?limit=10&cursor=…` lists your jobs, newest first (1–20 per page, follow `next_cursor`).

```json
{
  "id": "5f4a9a5e-fc45-4eae-9f9c-56b5f686cba5",
  "kind": "chart",
  "status": "succeeded",
  "created_at": "2026-10-14T11:20:05.000Z",
  "credits": { "reserved": 50, "charged": 50, "refunded": 0 },
  "outputs": [
    {
      "file_id": "0b7c…",
      "format": "png",
      "width": 2126,
      "height": 1417,
      "download_url": "https://scifig.ai/api/v1/files/0b7c…?exp=…&sig=…",
      "expires_at": "2026-10-14T12:21:40.000Z",
      "editor_url": "https://scifig.ai/app/datachart?conversation=…"
    }
  ],
  "error": null
}
```

`kind` is `image_generation`, `image_edit`, `image_upscale`, `image_conversion`, `chart` or `flowchart`. Responses never include your prompt, your inputs or internal identifiers.

| Output field | Present | Meaning |
| --- | --- | --- |
| file_id | always | Pass it to another endpoint or to `GET /files/{file_id}` |
| format | always | File extension of the output, such as `png`, `svg` or `pdf` |
| width, height | PNG outputs | Pixel size |
| download_url, expires_at | always | Signed link, valid for one hour |
| editor_url | chart and flowchart jobs | Opens the result in its website workspace; illustrations and conversions have none |

## Status lifecycle

| status | Meaning |
| --- | --- |
| queued | Accepted and waiting for capacity |
| processing | Running |
| succeeded | Done; `outputs` are ready and credits charged |
| failed | Did not produce a result; `error.code` says why and the reservation is refunded |
| canceled | Stopped before finishing; refunded |

| `error.code` of a failed job | Meaning | What to do |
| --- | --- | --- |
| TASK_TIMED_OUT | The task timed out and was refunded. An illustration generation or edit may carry `late_delivery_until`: until then the result can still arrive for free. | Keep reading the job until `late_delivery_until`; otherwise send a new request with a new key |
| CONTENT_REJECTED | The content policy refused the result while the task ran; refunded. The same code is returned as `422` when the request itself is refused at creation. | Change the prompt or the input and use a new key |
| ADMISSION_INCOMPLETE | The job was never started because a retryable error stopped it. No credits were used. | Send the same request with the same Idempotency-Key |
| TASK_NOT_FOUND | The job was admitted but its task can no longer be found. | Send a new request with a new key |
| TASK_FAILED | The task failed and was refunded. Every engine failure without a public code of its own becomes `TASK_FAILED`; the engine code is in `details.reason`. | Send a new request with a new key |
| INTERNAL_ERROR | The task failed without reporting a reason; refunded. | Send a new request with a new key |

`succeeded`, `failed` and `canceled` are final, with one exception: an illustration generation or edit that failed with `TASK_TIMED_OUT` carries `late_delivery_until`. Until then the result may still arrive, for free, and the job turns `succeeded`; keep reading it instead of creating a new one.

If any job in the list cannot be read, `GET /jobs` returns `503 SERVICE_UNAVAILABLE` with `Retry-After: 3`; retry the list request. It does not replace that job with a failed result.

## Bounded waiting

- `Prefer: wait=N` (1–30) on a create request holds the response until the job is final or N seconds pass. Final jobs return `200`; jobs still running return `202` with `Location: /api/v1/jobs/{id}` and `Retry-After`.
- Waiting does not change the job: a `202` is not an error, and the job keeps running after the response.
- Poll `GET /jobs/{id}` no faster than `Retry-After` (3 seconds). Illustrations usually finish in 20–90 seconds, charts and flowcharts in 30–120 seconds.
- A failed job may also hold a refused request: a create request refused with an error such as `402 INSUFFICIENT_CREDITS` or `422 CONTENT_REJECTED` stays in `GET /jobs` as a `failed` job with that code and no credits.
- Server integrations should store the job ID before waiting, so that a dropped connection never leads to a second job.

## cURL

```bash
curl https://scifig.ai/api/v1/jobs/<job_id> \
  -H "Authorization: Bearer $SCIFIG_API_KEY"
```

## JavaScript

```javascript
const response = await fetch('https://scifig.ai/api/v1/jobs/<job_id>', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.SCIFIG_API_KEY}`,
  },
});
const result = await response.json();
console.log(response.status, result);
```

## Python

```python
import os

import requests

headers = {
    "Authorization": f"Bearer {os.environ['SCIFIG_API_KEY']}",
}
response = requests.get(
    "https://scifig.ai/api/v1/jobs/<job_id>",
    headers=headers,
    timeout=60,
)
print(response.status_code, response.json())
```
