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

# Extraction

> Read a fixed set of typed values out of text and images with an extraction model, then store them or branch on them in a Flow.

An **Extract Fields** step reads named, typed values out of text and up to four images. You list the fields you want. Each field comes back as a value of its declared type, or as `null` when the input doesn't contain it. Every answer also carries the evidence a Flow needs to act on it: a confidence, the source span for text, and a calibrated range for an estimate.

An extraction model is not a chat model. There is no prompt, it generates no tokens, and it can't return a value of the wrong type. Use it when a message, email, form, CSV, or photographed receipt holds values that a Record, a branch, or a later step needs.

## Choose extraction, a judgment, or a prompt step

The three model-backed steps answer different kinds of question. Pick by the shape of the output you need:

| You need                                                                                                  | Use                                                                | What comes back                                                        |
| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| A value that the input states or implies: an order ID, a date, an amount, a list of items, a number range | **Extract Fields** step                                            | One typed value per field, or `null`, with confidence and source spans |
| A decision among options you list: refund or repair, which queue, a rubric score                          | A [judgment](/user-guide/flows/judgments) through a Tool Call step | A probability, a selected label, or a score                            |
| Prose, open-ended JSON whose keys you can't fix in advance, or work that needs tools                      | A [prompt step](/user-guide/flows/using-prompt-steps)              | Generated text or JSON                                                 |

Extraction returns the value. A judgment makes the decision. A prompt step writes the words. The three compose in that order: extract the facts, judge what to do, then write the reply. Only the rows that need a large model reach one.

A prompt step with **Response Format** set to **JSON** can also pull fields out of text. It is slower, it is billed on output tokens, and it doesn't tell you whether a value was in the input or how sure the model is. Use an extract step when the field list is fixed.

## What an extraction model can do

One extract step can mix four kinds of field. Each kind answers a different question about the input.

* **Extract a value that is written down.** A `text` field returns the exact characters from the input, with their position as a `span`. A `date` field returns an ISO date even when the input writes "Sept 28".
* **Compute a value that is not written down.** A `number` field with the instruction "Item price plus tax" returns the sum. A `duration` field returns the days between two dates. Keep each computation to one or two operations. Do longer arithmetic in a [Run Code](/user-guide/flows/using-transform-data-steps) step.
* **Estimate a value with a range.** An `estimate` field returns a point value and the 10th and 90th percentiles, for example a sale price from a table of comparable sales. The range is the signal: a wide range means thin evidence.
* **Forecast a series.** A `forecast` field returns one estimate per future period, for a horizon of 1 to 64 periods, from the history in the input.

When the input doesn't determine a field, the answer is `null`, not a guess. A list with no matches comes back as an empty array.

## Worked example: read a support email

A customer sends this email to a Flow, which receives it as `message`:

**`message`**

```plaintext title="message"
Hi, order A-88412 arrived on Sept 28, 2026 with a cracked screen and a dented
corner. I paid $649.00 plus $52.10 tax. It's now Oct 9, 2026 and I'd like this
fixed or refunded. Thanks, Dana
```

The following extract step asks for six fields:

**`Extract step`**

```json title="Extract step"
{
  "type": "extract",
  "name": "Read the claim",
  "config": {
    "state": "{{message}}",
    "fields": {
      "order_id": { "type": "text", "instructions": "The order number" },
      "delivered_on": { "type": "date" },
      "amount_paid": {
        "type": "number",
        "decimals": 2,
        "unit": "USD",
        "instructions": "Total paid including tax"
      },
      "days_since_delivery": { "type": "duration", "unit": "days", "integer": true },
      "damage": {
        "type": "list",
        "items": "text",
        "instructions": "Each kind of damage described"
      },
      "tracking_number": { "type": "text" }
    },
    "outputVariable": "claim",
    "errorHandling": { "onError": "fail" }
  }
}
```

Each field shows a different behavior:

* `order_id` and `delivered_on` are read from the text.
* `amount_paid` is computed: 649.00 plus 52.10 is 701.10. The instruction asks for the computation.
* `days_since_delivery` is computed from two dates in the email: 11 days.
* `damage` is a list of two spans of the text.
* `tracking_number` is `null`, because the email has none.

The step writes one object to `claim`. The following output shows its shape. The confidence and span values are illustrative:

**`claim`**

```json title="claim"
{
  "model": "@cf/cloudflare/clairic",
  "values": {
    "order_id": "A-88412",
    "delivered_on": "2026-09-28",
    "amount_paid": 701.1,
    "days_since_delivery": 11,
    "damage": ["cracked screen", "dented corner"],
    "tracking_number": null
  },
  "answers": {
    "order_id": {
      "type": "text",
      "value": "A-88412",
      "confidence": 0.99,
      "pNull": 0.01,
      "span": [10, 17]
    },
    "damage": {
      "type": "list",
      "value": ["cracked screen", "dented corner"],
      "spans": [[50, 64], [71, 84]]
    },
    "tracking_number": { "type": "text", "value": null, "pNull": 0.99 }
  },
  "usage": { "inputTokens": 180, "outputTokens": 0, "passes": 1 },
  "truncated": false
}
```

The output has two halves:

* `values` is a flat map with one entry per field. It is the object that a Record stores.
* `answers` keeps the evidence for each field. A branch reads it. The sample shows three of the six answers.

## Route on null and confidence, then store

Decide what to do with uncertain rows before you write anything. Store a claim only when its key fields are present and confident. Send the others to a person.

The following steps follow the extract step. The conditional sends a claim to review when its order ID is missing or its amount is not confident. Otherwise, the Flow stores the claim:

**`Conditional and Upsert Record steps`**

```json title="Conditional and Upsert Record steps"
{
  "type": "conditional",
  "name": "Route uncertain claims",
  "config": {
    "condition": "claim.values.order_id === null || !(claim.answers.amount_paid.confidence >= 0.9)",
    "trueSteps": [
      {
        "type": "send-stream",
        "name": "Needs review",
        "config": { "message": "A person will review this claim." }
      }
    ],
    "falseSteps": [
      {
        "type": "upsert-record",
        "name": "Store the claim",
        "config": {
          "recordType": "claim",
          "recordName": "{{claim.values.order_id}}",
          "sourceVariable": "claim.values",
          "outputVariable": "stored"
        }
      }
    ]
  }
}
```

Four details in this pattern matter:

* **Write conditions with bare variable paths**, such as `claim.answers.tracking_number.value === null`. A `{{...}}` reference is replaced with text before the condition runs, so it can't compare against `null`.
* **Write `!(confidence >= 0.9)`, not `confidence < 0.9`.** An answer can omit `confidence`. The negated form sends a missing confidence to review. The `< 0.9` form lets it through.
* **Set `sourceVariable` to the path `claim.values`, not `{{claim.values}}`.** Upsert Record reads the source as a variable path and doesn't substitute templates. Validation reports a template there as [`UPSERT_SOURCE_TEMPLATE`](/user-guide/flows/flow-validation-warnings#upsert-record-source-variable-is-a-template).
* **Key the Record on a value you routed on.** Without a `recordName`, each run creates a Record. A `null` order ID would give the Record an unusable name, which is why the condition checks it first.

The default **Merge** strategy writes every top-level key in `values`, including `null` ones. A `null` therefore replaces a value that an earlier run stored. To keep stored values, add a **Run Code** step that drops the `null` entries, and point `sourceVariable` at its output. For more about merge strategies, see [Using record steps](/user-guide/flows/using-record-steps-upsert-retrieve#choose-a-merge-strategy).

To choose between refund and repair, add a [judgment](/user-guide/flows/judgments) in the store branch and pass it the extracted values as its state. Then add a prompt step that writes the reply from the decision and the facts.

> **Note**
>
> The `0.9` threshold is a placeholder. Measure confidence against labeled examples from your own data before a threshold acts without review. The action policy stays in the Flow, not in the model.

## Add an extract step in the dashboard

To add an extract step, follow these steps:

1. Open the Flow and add an **Extract Fields** step.
2. In **Text to read**, reference the text, for example `{{message}}`. Text answers are spans of this text, so pass the whole message or document, not a summary.
3. Under **Fields**, click **Add field** for each field. Enter a name, pick a type, and add instructions when the name alone is ambiguous or you want a computation.
4. In **Writes to**, enter the output variable. Later steps read the result from it.
5. Optional: Pick a different model in the step settings, or expand **Images, reference date** to add images or set the date that "today" means.

The field name is sent to the model, so a descriptive name such as `order_id` is a complete field. A one- or two-character name with no instructions tells the model nothing, and validation recommends a better one.

## Field types

The following table lists each type, what it returns, and the options it accepts:

| Type                       | Returns                                                        | Options                                                                                               |
| -------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `text`                     | A span of the input text, with character offsets in `span`     | None                                                                                                  |
| `number`                   | A read or computed number                                      | `integer`, `decimals` (0 to 6), `unit`                                                                |
| `date`, `time`, `datetime` | An ISO value                                                   | None                                                                                                  |
| `duration`                 | A length of time                                               | `unit` (`seconds`, `minutes`, `hours`, `days`, `weeks`, `months`, or `years`), `integer`, `decimals`  |
| `list`                     | The matching items in input order, with `spans` for text items | `items` (`text`, `number`, or `date`; required), `decimals`                                           |
| `estimate`                 | A distribution: `value`, `mean`, `median`, `p10`, and `p90`    | `min` and `max` (required), `step`, `scale` (`linear` or `log`), `unit`, `decimals`                   |
| `forecast`                 | One estimate per future period, in `steps`                     | `min`, `max`, `horizon` (1 to 64), and `interval` (all required), `step`, `scale`, `unit`, `decimals` |

Every field also accepts `instructions` and `nullThreshold`. `nullThreshold` is a number greater than 0 and at most 1, with a default of 0.5. A scalar whose `pNull` is above it comes back as `null`.

Validation rejects an option that a type doesn't take, such as `decimals` on a `text` field.

### Estimate and forecast options

An estimate needs evidence in the input text, such as comparable sales or past values. Its range can be too narrow on data unlike what the model has seen.

* `min` and `max` bound the answer. They are numbers, or ISO dates for a date estimate.
* `step` is the resolution: a positive number, a number of days for a date estimate, or `month` or `year`.
* `scale: "log"` suits values that span orders of magnitude, such as prices.
* `interval` is the forecast's period: `step`, `minute`, `hour`, `day`, `week`, `month`, `quarter`, or `year`.

The following fields ask for an estimate and a six-month forecast:

**`Estimate and forecast fields`**

```json title="Estimate and forecast fields"
{
  "sale_price": {
    "type": "estimate",
    "min": 50000,
    "max": 2000000,
    "scale": "log",
    "step": 1000,
    "unit": "USD",
    "instructions": "Likely sale price of the subject property"
  },
  "monthly_orders": {
    "type": "forecast",
    "min": 0,
    "max": 100000,
    "horizon": 6,
    "interval": "month",
    "instructions": "Orders per month, from the monthly history in the text"
  }
}
```

In `values`, an estimate is its point value, and a forecast is an array with one point value per period. A forecast extends the history in the text. It is not a time-series model, so check its ranges against outcomes before a Flow acts on them.

## Configuration reference

The following table lists the fields of an extract step's `config`:

| Field            | Required | Description                                                                                                                                                            |
| ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `state`          | Yes      | A template for the text to read, for example `{{message}}`.                                                                                                            |
| `fields`         | Yes      | A map of field name to field spec, with at least one field. A name starts with a letter or `_`, uses letters, digits, `_`, `.`, or `-`, and has at most 64 characters. |
| `outputVariable` | Yes      | The variable that receives the result.                                                                                                                                 |
| `model`          | No       | An extraction model ID. Defaults to `@cf/cloudflare/clairic`.                                                                                                          |
| `images`         | No       | Up to four images. Each is a data URL, or a `{{variable}}` that resolves to a data URL or a `{ "contentType": "...", "base64": "..." }` object.                        |
| `referenceDate`  | No       | An ISO date that anchors dates written without a year. Accepts templates. Defaults to the date that the run started.                                                   |
| `errorHandling`  | No       | `{ "onError": "fail" }` or `{ "onError": "continue" }`.                                                                                                                |
| `defaultValue`   | No       | The value written to `outputVariable` when the step continues after an error.                                                                                          |

The output object has the following fields:

| Field       | Description                                                                                                                                                                                                                          |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model`     | The model that answered.                                                                                                                                                                                                             |
| `values`    | One entry per field: the value, `null`, an array for a list, the point value for an estimate, or an array of point values for a forecast.                                                                                            |
| `answers`   | One answer per field. Each has `type` and `value`. Depending on the type, it also has `confidence`, `pNull`, `span` or `spans`, the estimate fields (`mean`, `median`, `p10`, `p90`, `pBelow`, `pAbove`), or `steps` for a forecast. |
| `usage`     | `inputTokens`, `outputTokens` (always 0), and `passes`.                                                                                                                                                                              |
| `truncated` | `true` when the text did not fit the model's context and was cut.                                                                                                                                                                    |

## Images

An extract step reads images together with the text. The model accepts PNG, JPEG, and WebP.

* Pass at most four images. Each can be 4 MiB decoded, and all images together 8 MiB.
* Pass each image as a data URL, or as a variable that holds a data URL or a `{ contentType, base64 }` object.
* The step refuses remote image URLs and asset IDs. To read an image from a URL, fetch it in an earlier step and pass the data.

## Models, limits, and cost

`GET /v1/extraction-models` lists the supported extraction models, their limits, their pricing, and their credential requirements. The MCP tool `list_extraction_models` takes no arguments and returns the same list. Code Mode exposes it as `runtype.listExtractionModels()`. The catalog describes supported models. It doesn't check whether your account can pay for a call.

The following table lists the facts for the default model:

| Fact         | `@cf/cloudflare/clairic`                                            |
| ------------ | ------------------------------------------------------------------- |
| Route        | Cloudflare Workers AI, through the Runtype platform binding         |
| Context      | 65,536 tokens. Each pass holds 8,192 tokens.                        |
| Images       | Four, 4 MiB each, 8 MiB in total                                    |
| Listed price | \$0.10 per million input tokens. No output charge.                  |
| Credentials  | Runtype platform funding only. You can't use your own provider key. |

Fields that don't fit beside the text in one pass are split across parallel passes, and every pass reads the whole text. `usage.passes` reports how many ran. If the text is longer than the context, the step cuts it, sets `truncated` to `true`, and still succeeds with a warning on the step result.

Runtype bills a platform-funded extraction at the model's listed rate plus the platform margin. If your account's platform funding is blocked or exhausted, the step fails with a spend-limit error, and the step's `errorHandling` applies. An organization that requires strict zero data retention can't use this route, and its extract steps fail.

> **Note**
>
> Clairic is a Cloudflare preview model. Cloudflare reports that it returns the same answers for the same input. Test it on your own documents before a Flow depends on it.

## Design rules

Follow these rules to get reliable answers:

* **Put the whole document in `state`.** Text answers are spans of that string. A summary from a prompt step loses the source and the spans.
* **Say what to compute.** Add instructions such as "Item price plus tax" for a value that isn't written down.
* **Compute in code beyond one or two operations.** A sum of two amounts or a day count is reliable. A total across many rows is not. Extract the rows as a list and add them in a Run Code step.
* **Set `referenceDate` when "today" matters.** Relative dates and dates without a year are anchored on it. It defaults to the run's start date.
* **Don't ask the model to invent missing values.** Branch on `null` instead.
* **Give estimates evidence.** Include comparable items or history in the text.

## Errors and validation

Saving a Flow reports these codes for an extract step:

| Code                                                                                                                             | Level          | Meaning                                                                                          |
| -------------------------------------------------------------------------------------------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------ |
| [`EXTRACT_FIELDS_INVALID`](/user-guide/flows/flow-validation-warnings#extract-step-field-map-is-invalid)                         | Error          | A field's name, type, or options are wrong. Validation reports one message for each broken rule. |
| [`EXTRACT_MODEL_UNKNOWN`](/user-guide/flows/flow-validation-warnings#extract-step-names-a-model-that-is-not-an-extraction-model) | Error          | `model` is not an extraction model. A chat or decision model can't run the step.                 |
| [`EXTRACT_IMAGES_TOO_MANY`](/user-guide/flows/flow-validation-warnings#extract-step-lists-too-many-images)                       | Error          | The step lists more images than the model reads.                                                 |
| [`EXTRACT_STATE_EMPTY`](/user-guide/flows/flow-validation-warnings#extract-step-has-nothing-to-read)                             | Warning        | `state` is empty and the step has no images.                                                     |
| [`EXTRACT_FIELD_UNDESCRIBED`](/user-guide/flows/flow-validation-warnings#extract-field-says-nothing-to-the-model)                | Recommendation | A field has a one- or two-character name and no instructions.                                    |

At run time, the step treats two kinds of failure differently:

* **Input problems** are an empty resolved text with no images, an image that is too large or not a data URL, an unknown model, or a malformed field map. With `errorHandling` unset, the step reports a failure and writes no output. With `continue`, it writes `defaultValue`. With `fail`, it stops the Flow.
* **Model and funding failures** follow `errorHandling`. With `errorHandling` unset, the step continues and writes `defaultValue`, or `null` when you set none.

A later step that reads `claim.values` fails when `claim` is `null`. Set `errorHandling` to `{ "onError": "fail" }` on the extract step, or check for `null` before you read the output.

## Self-hosted runtimes

A self-hosted `@runtypelabs/runtime` answers the step through its `ExtractionProvider`. The key mode picks the provider:

* **`own`**: The runtime calls the Workers AI `AI` binding of your deployment.
* **`platform`**: The runtime calls `POST /v1/runtime/extractions` with a Self-hosted Runtime key. Runtype funds and bills the call.
* **`hybrid`**: The runtime tries the `AI` binding first. It calls Runtype only when no binding is configured.

For setup, see [Self-hosting](/developer-guides/guides/self-hosting#extract-step).

## Next steps

Continue with one of these guides:

* [Judgments](/user-guide/flows/judgments): make a decision from the values you extracted.
* [Using conditional steps](/user-guide/flows/using-conditional-steps): write the conditions that route on confidence.
* [Using record steps](/user-guide/flows/using-record-steps-upsert-retrieve): store `values` as a Record.
* [Flow validation warnings](/user-guide/flows/flow-validation-warnings): fix each validation code.