> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runpulse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Fill Form

## Overview

<Info>
  Fill a PDF form from natural-language instructions. Returns a `FormResult` synchronously by default (with a `pdf_url` you can `GET` to download the filled PDF). Set `async: true` to run in the background and poll [GET /job{'{'}jobId{'}'}](/api-reference/endpoint/poll) for the result.
</Info>

`/form/fill` writes values into the fields of a PDF form based on a natural-language `instructions` prompt. It works on both PDFs with native form fields (where the values are written directly into the form) and on flat or scanned PDFs (where the values are placed into the detected fields).

### Providing the PDF

Provide the PDF in **exactly one** of the following ways:

* `form_id`: chain off a prior [`/form/detect`](/api-reference/endpoint/form-detect), `/form/fill`, or [`/form/clear`](/api-reference/endpoint/form-clear) call. The cached PDF and `form_fields` are reused, so there is no need to re-upload.
* `file_url`: public or presigned URL to a PDF.
* `file`: PDF uploaded inline with the request.

Sending more than one (or none) returns `400`.

<Note>
  All three input modes ride on the same `multipart/form-data` request body — that's how the SDKs send every call. JSON bodies (`Content-Type: application/json`) with `form_id` or `file_url` are still accepted server-side for backward compatibility, but the SDKs only model the multipart form.
</Note>

### Pricing

Billed at **3 credits per page** of the PDF being filled. Every response also returns a top-level `credits_used` for **this request** and a cumulative `plan_info.total_credits_used` snapshot for your organization.

***

## Request

### Request Body

| Field          | Type                                              | Required     | Description                                                                                                                           |
| -------------- | ------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `form_id`      | string (uuid)                                     | One of these | Reuse a previously processed form. Skips re-upload and re-detection.                                                                  |
| `file_url`     | string (uri)                                      | One of these | Public or presigned URL of a PDF to download and fill.                                                                                |
| `file`         | binary                                            | One of these | PDF uploaded inline with the request.                                                                                                 |
| `instructions` | string                                            | Yes          | Natural-language description of what to fill into the form. Example: `"Use John Doe, 123 Main St, born 1990-01-01"`.                  |
| `form_fields`  | array of [`FormCell`](#response.body.form_fields) | No           | Optional override for the cells used when filling. Useful when the caller has hand-edited the cells returned by `/form/detect`.       |
| `page_range`   | string                                            | No           | 1-based page filter, for example `"1,3-5"`. Alias `pages` accepted.                                                                   |
| `async`        | boolean                                           | No           | When `true`, returns `{ job_id, status: "pending" }` immediately (HTTP 202) and processes the job in the background. Default `false`. |

***

## Response

### Sync (200): `FormResult`

When `async` is `false` (default), the call returns a `FormResult` body directly.

| Field           | Type                                              | Description                                                                                                                                                                                                                                         |
| --------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `form_id`       | string (uuid)                                     | ID of the new form record produced by this run. Pass back via `form_id` to chain further fills, clears, or detects.                                                                                                                                 |
| `page_count`    | integer                                           | Number of pages in the output PDF.                                                                                                                                                                                                                  |
| `pdf_url`       | string (uri)                                      | URL to download the filled PDF binary. Always points at [GET /results/{'{'}jobId{'}'}/pdf](/api-reference/endpoint/poll). Requires the same auth (API key or JWT) as the rest of the API and only serves results owned by the calling organization. |
| `form_fields`   | array of [`FormCell`](#response.body.form_fields) | Detected cells of the resulting (filled) PDF, refreshed after the fill.                                                                                                                                                                             |
| `fields_filled` | integer                                           | Number of cells whose value actually changed during this run (no-op writes are not counted).                                                                                                                                                        |
| `credits_used`  | number                                            | Credits consumed by this request (`3 × page_count`).                                                                                                                                                                                                |
| `plan_info`     | object                                            | `{ tier, total_credits_used, pages_used }` cumulative billing snapshot for your organization (post-request).                                                                                                                                        |

```json theme={null}
{
  "form_id": "00e2c454-4e6f-429b-bd74-320ad94b2153",
  "page_count": 6,
  "pdf_url": "https://api.runpulse.com/results/dab7285d-8a65-4cb6-9d24-d5db64d3798e/pdf",
  "form_fields": [
    {
      "page_number": 1,
      "type": "text",
      "bounding_box": [0.118, 0.226, 0.634, 0.241],
      "text": "Acme Logistics LLC"
    },
    {
      "page_number": 1,
      "type": "checkbox",
      "bounding_box": [0.118, 0.226, 0.634, 0.241],
      "text": "Individual/sole proprietor C corporation S corporation Partnership",
      "checkbox_details": [
        { "center_coord": [0.125, 0.232], "selected": true,  "text": "Individual/sole proprietor" },
        { "center_coord": [0.300, 0.232], "selected": false, "text": "C corporation" },
        { "center_coord": [0.418, 0.232], "selected": false, "text": "S corporation" },
        { "center_coord": [0.535, 0.232], "selected": false, "text": "Partnership" }
      ]
    }
  ],
  "fields_filled": 7,
  "credits_used": 18.0,
  "plan_info": {
    "tier": "pulse_ultra_2",
    "total_credits_used": 1284.0,
    "pages_used": 428
  }
}
```

<Note>
  All cell coordinates (`bounding_box`, `checkbox_details[].center_coord`) are normalized to `[0, 1]` with a top-left origin. Multiply by your render width / height to convert to pixel coordinates.
</Note>

### Async (202): `FormJobAccepted`

When `async` is `true`:

```json theme={null}
{
  "job_id": "abc123-def456-ghi789",
  "status": "pending"
}
```

Poll [GET /job/{'{'}jobId{'}'}](/api-reference/endpoint/poll). The job's `result` carries the same `FormResult` shape that the sync flow would have returned inline.

### Status Codes

| Code | Description                                                                                         |
| ---- | --------------------------------------------------------------------------------------------------- |
| 200  | Filled `FormResult` returned synchronously.                                                         |
| 202  | Async job accepted (`async: true`). Poll `/job/{jobId}` for the result.                             |
| 400  | Missing PDF, more than one PDF source provided, missing `instructions`, or malformed `form_fields`. |
| 401  | Authentication failed or missing API key.                                                           |
| 404  | Referenced `form_id` not found (or belongs to a different org).                                     |
| 500  | Internal server error.                                                                              |

***

## Example Usage

### Fill From URL

<CodeGroup>
  ```python Python theme={null}
  from pulse import Pulse

  client = Pulse(api_key="YOUR_API_KEY")

  result = client.form.fill(
      file_url="https://example.com/intake-form.pdf",
      instructions="Fill in patient name as Jane Doe, DOB 01/15/1990.",
  )

  print(f"form_id={result.form_id}")
  print(f"fields_filled={result.fields_filled}")
  print(f"credits_used={result.credits_used}")
  print(f"download: {result.pdf_url}")
  ```

  ```typescript TypeScript theme={null}
  import { PulseClient } from "pulse-ts-sdk";

  const client = new PulseClient({ apiKey: "YOUR_API_KEY" });

  const result = await client.form.fill({
      file_url: "https://example.com/intake-form.pdf",
      instructions: "Fill in patient name as Jane Doe, DOB 01/15/1990.",
  });

  console.log(`form_id=${result.form_id}`);
  console.log(`fields_filled=${result.fields_filled}`);
  console.log(`download: ${result.pdf_url}`);
  ```

  ```bash curl theme={null}
  curl -X POST https://api.runpulse.com/form/fill \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "file_url": "https://example.com/intake-form.pdf",
      "instructions": "Fill in patient name as Jane Doe, DOB 01/15/1990."
    }'
  ```
</CodeGroup>

### File Upload

<CodeGroup>
  ```python Python theme={null}
  with open("intake-form.pdf", "rb") as f:
      result = client.form.fill(
          file=f,
          instructions="Fill in patient name as Jane Doe, DOB 01/15/1990.",
      )
  ```

  ```typescript TypeScript theme={null}
  import * as fs from "fs";

  const fileBuffer = fs.readFileSync("intake-form.pdf");
  const blob = new Blob([fileBuffer], { type: "application/pdf" });

  const result = await client.form.fill({
      file: blob,
      instructions: "Fill in patient name as Jane Doe, DOB 01/15/1990.",
  });
  ```

  ```bash curl theme={null}
  curl -X POST https://api.runpulse.com/form/fill \
    -H "x-api-key: YOUR_API_KEY" \
    -F "file=@intake-form.pdf" \
    -F "instructions=Fill in patient name as Jane Doe, DOB 01/15/1990."
  ```
</CodeGroup>

### Detect First, Then Fill

Run [`/form/detect`](/api-reference/endpoint/form-detect) to inspect the detected cells, optionally edit them, then chain a fill that reuses the same `form_id`. There is no need to re-upload the PDF.

<CodeGroup>
  ```python Python theme={null}
  detect = client.form.detect(file_url="https://example.com/intake-form.pdf")

  # (Optional) edit detected cells locally, e.g. retype a misclassified cell
  edited = []
  for cell in detect.form_fields or []:
      if cell.text and cell.text.strip().lower() == "signature":
          cell.type = "signature"
      edited.append(cell)

  result = client.form.fill(
      form_id=detect.form_id,
      instructions="Fill in patient name as Jane Doe, DOB 01/15/1990.",
      form_fields=edited,  # omit to use the cached cells from detect
  )
  ```

  ```typescript TypeScript theme={null}
  const detect = await client.form.detect({
      file_url: "https://example.com/intake-form.pdf",
  });

  const edited = (detect.form_fields ?? []).map((cell) =>
      cell.text?.trim().toLowerCase() === "signature"
          ? { ...cell, type: "signature" as const }
          : cell,
  );

  const result = await client.form.fill({
      form_id: detect.form_id!,
      instructions: "Fill in patient name as Jane Doe, DOB 01/15/1990.",
      form_fields: edited,
  });
  ```

  ```bash curl theme={null}
  # Step 1: detect
  curl -X POST https://api.runpulse.com/form/detect \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"file_url": "https://example.com/intake-form.pdf"}'

  # Step 2: fill via the form_id from step 1
  curl -X POST https://api.runpulse.com/form/fill \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "form_id": "<form_id from step 1>",
      "instructions": "Fill in patient name as Jane Doe, DOB 01/15/1990."
    }'
  ```
</CodeGroup>

### Async Fill With Polling

Use `async: true` for long-running jobs (large PDFs, multi-page fills) so the client does not have to keep a connection open.

<CodeGroup>
  ```python Python theme={null}
  import time

  submission = client.form.fill(
      file_url="https://example.com/big-form.pdf",
      instructions="Fill the form for Jane Doe ...",
      async_=True,  # SDK aliases the reserved keyword
  )

  while True:
      job = client.jobs.get_job(job_id=submission.job_id)
      if job.status in ("completed", "failed"):
          break
      time.sleep(2)

  result = job.result  # same FormResult body as the sync flow
  print(f"fields_filled={result['fields_filled']} pdf_url={result['pdf_url']}")
  ```

  ```typescript TypeScript theme={null}
  const submission = await client.form.fill({
      file_url: "https://example.com/big-form.pdf",
      instructions: "Fill the form for Jane Doe ...",
      async: true,
  });

  let job = await client.jobs.getJob({ jobId: submission.job_id! });
  while (job.status !== "completed" && job.status !== "failed") {
      await new Promise((r) => setTimeout(r, 2000));
      job = await client.jobs.getJob({ jobId: submission.job_id! });
  }

  const result = job.result as Record<string, unknown>;
  console.log(`fields_filled=${result.fields_filled} pdf_url=${result.pdf_url}`);
  ```

  ```bash curl theme={null}
  # Submit async
  curl -X POST https://api.runpulse.com/form/fill \
    -H "x-api-key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "file_url": "https://example.com/big-form.pdf",
      "instructions": "Fill the form for Jane Doe ...",
      "async": true
    }'

  # Poll
  curl https://api.runpulse.com/job/<job_id> \
    -H "x-api-key: YOUR_API_KEY"
  ```
</CodeGroup>

### Download The Filled PDF

The `pdf_url` returned in `FormResult` points at `GET /results/{job_id}/pdf` and requires authentication (API key or JWT).

<CodeGroup>
  ```python Python theme={null}
  job_id = result.pdf_url.rstrip("/").split("/")[-2]
  with open("filled.pdf", "wb") as out:
      for chunk in client.results.get_pdf(job_id=job_id):
          out.write(chunk)
  ```

  ```typescript TypeScript theme={null}
  const jobId = result.pdf_url!.replace(/\/$/, "").split("/").slice(-2, -1)[0];
  const pdfStream = await client.results.getPdf({ jobId });
  // pdfStream is a ReadableStream<Uint8Array>; write to disk however you prefer.
  ```

  ```bash curl theme={null}
  curl https://api.runpulse.com/results/<job_id>/pdf \
    -H "x-api-key: YOUR_API_KEY" \
    --output filled.pdf
  ```
</CodeGroup>


## OpenAPI

````yaml POST /form/fill
openapi: 3.1.0
info:
  title: Pulse API
  description: >-
    Production-grade document extraction service that transforms complex
    documents  into structured, AI-ready data. This specification is the single
    source of truth  for the Pulse extraction APIs.
  version: 1.0.0
  contact:
    name: Pulse Support
    email: support@trypulse.ai
    url: https://docs.runpulse.com
servers:
  - url: https://api.runpulse.com
    description: Production server
security:
  - ApiKeyAuth: []
paths:
  /form/fill:
    post:
      tags:
        - Forms
      summary: Fill PDF Form
      operationId: formFill
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FormFillRequest'
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/FormFillMultipart'
      responses:
        '200':
          description: Filled `FormResult` returned synchronously.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormResult'
        '202':
          description: >-
            Async job accepted (`async: true`). Poll `GET /job/{jobId}` for the
            result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FormJobAccepted'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  schemas:
    FormFillRequest:
      type: object
      description: |
        JSON body for `POST /form/fill`. Provide exactly one of `form_id` or
        `file_url` (or use the multipart variant to upload a `file`).
      required:
        - instructions
      properties:
        form_id:
          type: string
          format: uuid
          description: |
            ID returned by a previous `/form/detect`, `/form/fill`, or
            `/form/clear` call. Reuses the stored PDF and detected fields
            without re-uploading.
        file_url:
          type: string
          format: uri
          description: Public or presigned URL of a PDF to download and fill.
        instructions:
          type: string
          description: |
            Required natural-language description of what to fill into the
            form (e.g. `"Use John Doe, 123 Main St, born 1990-01-01"`).
          example: Fill in patient name as Jane Doe, DOB 01/15/1990.
      allOf:
        - $ref: '#/components/schemas/FormFillSharedOptions'
    FormFillMultipart:
      type: object
      description: Multipart `/form/fill` request with a direct PDF upload.
      properties:
        file:
          type: string
          format: binary
          description: PDF file to upload directly.
        file_url:
          type: string
          format: uri
        form_id:
          type: string
          format: uuid
        instructions:
          type: string
          description: Required natural-language fill prompt.
        form_fields:
          type: string
          description: |
            Optional JSON-encoded array of `FormCell` objects to override
            detected cells. Multipart bodies must serialise this field as
            a string.
        page_range:
          type: string
        async:
          type: string
          description: Set to `"true"` to run asynchronously.
    FormResult:
      type: object
      description: |
        Result body returned by `/form/detect`, `/form/fill`, and
        `/form/clear`. For async jobs (`async: true`) the same shape is
        served back under `result` on `GET /job/{jobId}`.
      required:
        - form_id
        - page_count
        - form_fields
      properties:
        form_id:
          type: string
          format: uuid
          description: |
            ID of the form record produced by this run. Pass to a subsequent
            `/form/detect`, `/form/fill`, or `/form/clear` call as the single
            input source to iterate without re-uploading the PDF.
        page_count:
          type: integer
          minimum: 1
          description: Number of pages in the output PDF.
        pdf_url:
          type: string
          description: |
            URL to download the resulting PDF binary. Always points at
            `GET /results/{jobId}/pdf` for the originating job. Requires the
            same authentication (API key or JWT) as the rest of the API.
        form_fields:
          type: array
          description: |
            Detected cells of the resulting PDF (refreshed from the
            filled / cleared output for fill / clear, or freshly detected for
            `/form/detect`).
          items:
            $ref: '#/components/schemas/FormCell'
        fields_filled:
          type: integer
          minimum: 0
          description: |
            Number of cells whose value actually changed during this run.
            Present on `/form/fill` responses only.
        fields_cleared:
          type: integer
          minimum: 0
          description: |
            Number of cells whose value actually changed during this run
            (no-op clears on already-empty fields are not counted).
            Present on `/form/clear` responses only.
        credits_used:
          type: number
          format: float
          description: |
            Credits consumed by **this request**. Detect charges 1 credit
            per page; fill and clear charge 3 credits per page.
        plan_info:
          $ref: '#/components/schemas/FormPlanInfo'
    FormJobAccepted:
      type: object
      description: 'Async submission acknowledgement returned when `async: true`.'
      required:
        - job_id
        - status
      properties:
        job_id:
          type: string
          format: uuid
          description: Identifier for `GET /job/{jobId}` polling.
        status:
          type: string
          enum:
            - pending
    FormFillSharedOptions:
      type: object
      description: Cell-level overrides accepted by `/form/fill` and `/form/clear`.
      properties:
        form_fields:
          type: array
          description: |
            Optional override for the cells used when filling or clearing.
            When omitted, Pulse uses the cells stored on the referenced
            `form_id`, or detects them from the uploaded PDF.
          items:
            $ref: '#/components/schemas/FormCell'
      allOf:
        - $ref: '#/components/schemas/FormSharedOptions'
    FormCell:
      type: object
      description: >
        A single detected cell on a form page. Cells are produced by Pulse

        cell detection during a `/form/detect`, `/form/fill`, or `/form/clear`

        call and may be passed back via `form_fields` to override what Pulse

        uses when filling or clearing.


        All coordinate fields (`bounding_box`,
        `checkbox_details[].center_coord`)

        are normalized to `[0, 1]` relative to the page width / height, so

        they're independent of the renderer's pixel resolution. `page_number`

        is 1-indexed.
      properties:
        page_number:
          type: integer
          minimum: 1
          description: 1-indexed page the cell belongs to.
        bounding_box:
          type: array
          description: |
            Cell bounding box `[x1, y1, x2, y2]` normalized to the range
            `[0, 1]` (top-left origin).
          items:
            type: number
            minimum: 0
            maximum: 1
          minItems: 4
          maxItems: 4
        text:
          type: string
          description: Current text content of the cell. Empty string for unfilled cells.
        type:
          type: string
          description: Detected cell type.
          enum:
            - text
            - checkbox
            - signature
        row:
          type: integer
          minimum: 0
          description: >-
            Row index inside the detected table this cell belongs to (when
            applicable).
        col:
          type: integer
          minimum: 0
          description: >-
            Column index inside the detected table this cell belongs to (when
            applicable).
        table_idx:
          type: integer
          minimum: 0
          description: >-
            Index of the detected table on the page this cell belongs to
            (0-indexed).
        checkbox_details:
          type: array
          description: |
            Only present when `type == "checkbox"`. One entry per individual
            checkbox inside this cell. A single cell often contains multiple
            checkboxes (for example a "Filing Status" row with Single / MFJ /
            MFS / HoH / QSS). The cell-level `text` is the raw line covering
            the whole cell; per-box labels live here on each detail.
          items:
            $ref: '#/components/schemas/FormCheckboxDetail'
    FormPlanInfo:
      type: object
      description: |
        Cumulative billing snapshot for the calling organization. Includes
        the in-flight request's contribution, so every response reflects
        post-request state.
      properties:
        tier:
          type: string
          description: Billing tier, e.g. `"trial"`, `"pulse_ultra_2"`.
        total_credits_used:
          type: number
          format: float
          description: |
            Total credits consumed by the organization to date, including
            this request. The primary billing metric.
        pages_used:
          type: integer
          minimum: 0
          description: |
            Total pages processed by the organization to date, including
            this request. Kept for backward compatibility with clients
            that haven't migrated to `total_credits_used`.
    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Error code (e.g., FILE_001, AUTH_002)
            message:
              type: string
              description: Human-readable error message
            details:
              type: object
              description: Additional error context
    FormSharedOptions:
      type: object
      description: Optional knobs accepted by all three form endpoints.
      properties:
        page_range:
          type: string
          description: |
            Restrict the operation to a subset of pages. Accepts
            comma-separated page numbers and ranges, e.g. `"1-3,5"`.
            Alias: `pages`.
        async:
          type: boolean
          default: false
          description: |
            When `true`, the endpoint returns immediately with
            `{ job_id, status: "pending" }` (HTTP 202) and processes the
            job in the background. Poll `GET /job/{jobId}` for the result.
    FormCheckboxDetail:
      type: object
      description: |
        A single checkbox inside a `FormCell` of `type == "checkbox"`. Bundles
        the checkbox's normalized center coordinate, current selection state,
        and the per-box text label.
      properties:
        center_coord:
          type: array
          description: |
            Normalized `[x, y]` center of the checkbox (in `[0, 1]`, top-left
            origin), in the same coordinate system as `FormCell.bounding_box`.
          items:
            type: number
            minimum: 0
            maximum: 1
          minItems: 2
          maxItems: 2
        selected:
          type: boolean
          description: >-
            `true` when the checkbox is currently filled in (selected), `false`
            otherwise.
        text:
          type: string
          description: |
            Per-checkbox text label (e.g. `"Individual/sole proprietor"`,
            `"S corporation"`). May be an empty string when no label could be
            confidently associated with this specific box.
  responses:
    BadRequest:
      description: Bad request - Invalid parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Unauthorized - Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: API key for authentication

````