> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.cloudraker.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.cloudraker.com/_mcp/server.

# Fill

`POST /v1/fill` takes a blank form and the documents that hold the answers, drafts every field, and returns the completed PDF. With `review: "required"` the run parks before producing the file so a person can correct the draft first.

## How it works

1. You send a **template** — `{"id": "…"}` for a [saved template](/capabilities/templates), or `{"url": "…"}` for a one-off — and the **files** to draft from.
2. CloudRaker parses the sources, reads the form's fields (detecting them when the PDF has none), and drafts a value per field.
3. `review: "none"` (the default) writes the values straight into the PDF. `review: "required"` parks the run at `needs_input` until the values are submitted.
4. The completed file comes back as `output.file`, `flattened` (values baked in) or `editable` (still a fillable form).
5. The run and its files expire on their own (`ttl`, 24 hours by default) unless you [keep](/developers/runs#keep-a-run) them.

## Quickstart

The sample fills a blank IRS Form W-9 from one source document — both public URLs, nothing local.

```bash title="curl"
curl -X POST https://api.cloudraker.com/v1/fill \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template": { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf", "name": "w9.pdf" },
    "files": [{ "id": "a04d6597-4e34-4a99-94ea-964c289a4c68" }],
    "instructions": "Use the legal entity name, not the trade name.",
    "review": "none",
    "output": "flattened"
  }'
```

```ts title="TypeScript"
const res = await fetch("https://api.cloudraker.com/v1/fill", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLOUDRAKER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    template: { url: "https://www.irs.gov/pub/irs-pdf/fw9.pdf", name: "w9.pdf" },
    files: [{ id: "a04d6597-4e34-4a99-94ea-964c289a4c68" }],
    review: "none",
  }),
});

const run = await res.json();
console.log(run.status, run.output?.file?.name);
```

```python title="Python"
import os, requests

res = requests.post(
    "https://api.cloudraker.com/v1/fill",
    headers={"Authorization": f"Bearer {os.environ['CLOUDRAKER_API_KEY']}"},
    json={
        "template": {"url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf", "name": "w9.pdf"},
        "files": [{"id": "a04d6597-4e34-4a99-94ea-964c289a4c68"}],
        "review": "none",
    },
)

run = res.json()
print(run["status"], run.get("output", {}).get("file", {}).get("name"))
```

The TypeScript and Python samples are plain HTTP so they run with nothing installed. This endpoint is also a top-level method on both [SDKs](/developers/sdks) as of 0.3.0 — `client.fill(…)`; that page has a full worked example.

## Example response

```json
{
  "object": "fill_run",
  "id": "flr_01KYD75GGJ8QK6HN2TVZ0ABCDE",
  "status": "processed",
  "expiresAt": "2026-07-26T18:03:41.512Z",
  "statusUrl": "/v1/runs/flr_01KYD75GGJ8QK6HN2TVZ0ABCDE",
  "files": [
    { "id": "a04d6597-4e34-4a99-94ea-964c289a4c68", "name": "w9.pdf", "status": "processed" }
  ],
  "output": {
    "file": {
      "id": "6913da38-6d0c-4b6f-9a1e-2b0f6f2b8a41",
      "name": "w9-template (filled).pdf",
      "url": "https://cdn.cloudraker.com/…/latest?token=…"
    },
    "files": [
      { "id": "6913da38-6d0c-4b6f-9a1e-2b0f6f2b8a41", "name": "w9-template (filled).pdf", "url": "https://cdn.cloudraker.com/…/latest?token=…" }
    ],
    "fields": { "topmostSubform[0].Page1[0].f1_01[0]": "Acme Manufacturing Co." }
  }
}
```

## Key fields

| Field           | What it is                                                                                                                  |
| --------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `object`        | Always `fill_run`.                                                                                                          |
| `id`            | The run id (`flr_…`).                                                                                                       |
| `status`        | `queued`, `processing`, `needs_input`, `processed`, `failed`, `cancelled`, or `expired`.                                    |
| `files[]`       | The source documents, with per-file `status`.                                                                               |
| `output.file`   | The completed PDF — `{id, name, url}`. Also at `GET /v1/runs/:id/output/:name`.                                             |
| `output.fields` | The values written into the form, keyed by the PDF's own field names. Empty when the sources carried nothing for any field. |

The output file's bytes are registered a moment after the run reports `processed`. In that window `output.file` can arrive without its `url` and the `/output/` alias can answer `404` — re-fetch `GET /v1/runs/:id` and the URL will be there.

## Configuration

| Field          | Type                                                  | What it does                                                                                              |
| -------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `template`     | `{id}` or `{url, name?}`                              | **Required.** A saved [template](/capabilities/templates), or an ephemeral one fetched for this run only. |
| `files[]`      | array of `{url, name?, processing?}` or `{id}`, 1–100 | **Required.** The documents the values are drafted from.                                                  |
| `instructions` | string, ≤ 4000 chars                                  | Guidance for the draft ("use the 2025 fiscal year figures").                                              |
| `review`       | `none` \| `required`                                  | `none` (default) fills and finishes. `required` parks at `needs_input` for a human.                       |
| `output`       | `flattened` \| `editable`                             | `flattened` (default) bakes the values in; `editable` keeps the form fillable.                            |
| `action`       | string                                                | A [saved action](/capabilities/actions) — id or slug. Inline fields win over its saved config.            |
| `metadata`     | object                                                | Your own key/values, echoed back on the run body (not on webhook deliveries). Max 10 KB.                  |
| `webhook`      | `{url}` or `{id}`                                     | Where to deliver terminal events. See [Webhooks](/developers/webhooks).                                   |
| `ttl`          | integer seconds, 1–604800                             | How long the run and its files live. Default 24 hours, max 7 days.                                        |

An ephemeral `template: {url}` is fetched at run time and is not added to your template library. The fetched copy is registered as a file in your API workspace and listed by `GET /v1/files` while the run is alive, but it belongs to the run: it is reclaimed when the run expires, and `POST /v1/runs/:id/keep` moves it into the target space with everything else. Pass `{id}` when the same blank form is filled repeatedly — a saved template is persistent and is never touched by a run's TTL.

## Human review

With `review: "required"` the call returns `202` immediately and the run settles at `needs_input`:

```json
{
  "object": "fill_run",
  "id": "flr_01KYD7664TNP5JKHCPTKSP84MQ",
  "status": "needs_input",
  "expiresAt": "2026-07-26T18:04:22.338Z",
  "statusUrl": "/v1/runs/flr_01KYD7664TNP5JKHCPTKSP84MQ",
  "files": [
    { "id": "a04d6597-4e34-4a99-94ea-964c289a4c68", "name": "w9.pdf", "status": "processed" }
  ],
  "tasks": [
    {
      "id": "review",
      "title": "Review form values",
      "url": "https://action-fill-form.actions.cloudraker.com/tasks/b1c08db9…"
    }
  ]
}
```

`tasks[]` lists every human step blocking the run. Each `url` is a **hosted review page**: open it in a browser (or send it to whoever should review), correct the drafted values, and submit — the run produces the document and finishes. The link is unguessable and carries the whole capability, so treat it as a secret; it stops working once the task is submitted or the run expires.

Prefer to build your own review UI? Drive the same task over the API instead:

#### Read the task

`GET /v1/runs/:id/task` returns the drafted `values`, the `fields` inventory (name, type, label, page, box), and the template's `pageBoxes` / `pageCount` — everything needed to render your own review UI. `GET /v1/runs/:id/task/pdf` streams the exact template bytes those boxes were measured on. Both `404` once the task is submitted or expired.

#### Submit the reviewed values

`POST /v1/runs/:id/task` with `{ "values": { "<field name>": "<value>" } }`. Unknown field names are discarded, the PDF is produced, and the run finishes.

#### Read the result

`GET /v1/runs/:id` — `status` is `processed` and `output.file` carries the completed document.

```bash
curl -X POST https://api.cloudraker.com/v1/runs/flr_01KYD7664TNP5JKHCPTKSP84MQ/task \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "values": { "topmostSubform[0].Page1[0].f1_01[0]": "Acme Manufacturing Co." } }'
```

The hosted page and the task routes are two views of the same task — either one submits it, and whichever comes second `404`s. Runs started inside the product park identically but are reviewed in the app, so they carry no `tasks[]`.

## Sync vs async

Synchronous by default with `?wait=` between `0` and `120` seconds, exactly like [extract](/capabilities/extract#sync-vs-async). `review: "required"` is the exception: a run that parks for a human returns `202` immediately rather than holding the request.

## Save as an action

Save the instructions, review policy, output mode, and even the template once, then call `{"files": …, "action": "vendor-onboarding"}`:

```bash
curl -X POST https://api.cloudraker.com/v1/actions \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "capability": "fill",
    "name": "Vendor onboarding",
    "config": { "review": "required", "outputMode": "flattened" }
  }'
```

Use `GET /v1/actions/catalog` for the exact `config` schema of the `fill-form` slug — see [Saved actions](/capabilities/actions).

## Next steps

#### [Templates](/capabilities/templates)

Store a blank form once, inspect its fields, and fill it by id.

#### [Sign](/capabilities/sign)

Send the completed document out for signature.

#### [Runs](/developers/runs)

Statuses, downloading outputs, TTL, and keeping a result.

#### [Webhooks](/developers/webhooks)

Get told when a run parks or finishes instead of polling.