# Tracking jobs

Creating a form and filling from context are **asynchronous**: the API returns
right away with a `job_id` (or `form_id`) and a `status` of `processing`, then
the real work runs in the background. You track a job to completion by polling.

You can track a job two ways: **poll** for the result (below), or have Emboss
**call you** when the job finishes: see [Callbacks](/docs/callbacks). This guide
covers polling.

## Poll for a form's status

```bash
curl https://api.getemboss.ai/forms/6c47f7f5-f921-4698-910f-95dd7d81310b \
  -H "Authorization: Bearer sk_..."
```

`status` moves from `processing` to either `ready` or `failed`:

```json
{ "id": "6c47f7f5-f921-4698-910f-95dd7d81310b", "status": "ready" }
```

For a context-fill job, poll `GET /forms/with-context/{job_id}` the same way:
it returns `processing`, then `ready` with a `session_id`, or `failed` with an
`error`.

## A simple polling loop

Poll every second or two, and stop as soon as the status is terminal. Status
checks don't count toward the rate limit; the backoff below just keeps a long
wait polite.

```js
async function waitForForm(formId, key) {
  let delay = 1000; // start at 1s
  while (true) {
    const res = await fetch(`https://api.getemboss.ai/forms/${formId}`, {
      headers: { Authorization: `Bearer ${key}` },
    });
    const { status } = await res.json();
    if (status === "ready") return "ready";
    if (status === "failed") throw new Error("detection failed");
    await new Promise((r) => setTimeout(r, delay));
    delay = Math.min(delay * 1.5, 5000); // back off to 5s max
  }
}
```

## When a job fails

A `failed` status comes with an `error` describing why (for example, an
unreadable PDF). A failed job is not charged: its charges are taken back when
it fails, except the form's creation if the form was already made fillable
before the failure. Surface the error, fix the input, and resubmit. A failed
job isn't retried automatically.

## Jobs cut off by a restart

Work runs on Emboss's servers after your call answers. If a restart cuts a job
off, it can never finish, so Emboss marks it `failed` with code `interrupted`
once it has been stuck for 6 hours: a form still in detection 6 hours after it
was created, a fill job 6 hours after it last moved, or a batch 6 hours after
it started. A form's or fill job's charge is taken back; a batch keeps the
charge for the rows it filled, which you can still download. If you set a
callback, it reports the failure. Submit the job again.

## The result carries an artifact_id

A `ready` job's result carries an `artifact_id` for the file it produced, and
a context-fill or commit job carries `artifacts`, one entry per file, each
with its own `artifact_id` and a `role` such as `filled`, `receipt`, or
`package`. Hand that id straight to another operation, such as
[faxing the result](/docs/send-fax), without downloading anything first. See
[Artifacts and lineage](/docs/artifacts).

For the bigger picture of what runs between `processing` and `ready`, see
[How Emboss works](/docs/how-emboss-works).

## See also

- [Bulk-fill PDFs programmatically](https://getemboss.ai/use-cases/bulk-fill-pdfs)
- [PDF form](https://getemboss.ai/glossary/pdf-form)
- [Artifacts and lineage](https://getemboss.ai/docs/artifacts)
- [Usage & health](https://getemboss.ai/docs/reference/usage)
- [Errors & status codes](https://getemboss.ai/docs/reference/errors)
- [Fill PDF forms in n8n workflows](https://getemboss.ai/integrations/n8n)
- [Fill PDF forms in Pipedream workflows](https://getemboss.ai/integrations/pipedream)
- [Fill PDF forms in Make scenarios](https://getemboss.ai/integrations/make)
