> This page is for version v2026-02-09 (default).
> For other versions, use one of these documentation indexes:
> - v2026-02-09 (default): https://docs.extend.ai/2026-02-09/llms.txt
> - v2025-04-21: https://docs.extend.ai/2025-04-21/llms.txt
> - v2024-12-23: https://docs.extend.ai/2024-12-23/llms.txt

> ## Documentation Index
> Fetch the complete documentation index at: https://docs.extend.ai/llms.txt
> Use this file to discover all available pages before exploring further.
>
> ## API version
> The current API version is `2026-02-09`, served at the site root (no version prefix in URLs).
> If this page URL contains `/2025-04-21/` or `/2024-12-23/`, you are reading an older API version.
> Prefer the current docs at https://docs.extend.ai/llms.txt unless the user explicitly needs that older version.
> Do not treat older-version pages as the source of truth for new integrations.

# Extract Runs Migration

> Migrate from /processor_runs to /extract_runs

## What You Get

* **Fully typed SDK responses** — `extractRun.output` is typed, no more casting
* **Run without an extractor** — Pass your schema inline with `config` instead of creating an extractor first
* **Cleaner request/response format** — Simpler property names, predictable nullable fields
* **Better IDE experience** — Autocomplete works out of the box

> **Note**
>
> The old `/processor_runs` endpoint is still supported in this API version for backward compatibility. You can migrate incrementally.

---

## Quick Start: Common Patterns

### Running an Extraction

#### TypeScript

**`Before (2025-04-21)`**

```typescript title="Before (2025-04-21)"
const run = await client.processorRun.create({
  processorId: "dp_abc123",
  file: { fileUrl: "https://example.com/invoice.pdf" },
  sync: true
});
if (run.success) {
  console.log(run.processorRun.output);
}
```

**`After (2026-02-09)`**

```typescript title="After (2026-02-09)"
const result = await client.extract({
  extractor: { id: "ex_abc123" },
  file: { url: "https://example.com/invoice.pdf" }
});
console.log(result.output?.value);

// Override an extractor's config (config moved inside extractor object)
const result = await client.extract({
  extractor: {
    id: "ex_abc123",
    overrideConfig: { schema: { type: "object", properties: { vendorName: { type: ["string", "null"] } } } }
  },
  file: { url: "https://example.com/invoice.pdf" }
});

// Or with inline config (no extractor needed)
const result = await client.extract({
  config: { schema: { type: "object", properties: { vendorName: { type: ["string", "null"] } } } },
  file: { url: "https://example.com/invoice.pdf" }
});
```

#### Python

**`Before (2025-04-21)`**

```python title="Before (2025-04-21)"
run = client.processor_run.create(
    processor_id="dp_abc123",
    file={"file_url": "https://example.com/invoice.pdf"},
    sync=True
)
if run.success:
    print(run.processor_run.output)
```

**`After (2026-02-09)`**

```python title="After (2026-02-09)"
result = client.extract(
    extractor={"id": "ex_abc123"},
    file={"url": "https://example.com/invoice.pdf"}
)
print(result.output.value)

# Override an extractor's config (config moved inside extractor object)
result = client.extract(
    extractor={
        "id": "ex_abc123",
        "override_config": {"schema": {"type": "object", "properties": {"vendorName": {"type": ["string", "null"]}}}}
    },
    file={"url": "https://example.com/invoice.pdf"}
)

# Or with inline config (no extractor needed)
result = client.extract(
    config={"schema": {"type": "object", "properties": {"vendorName": {"type": ["string", "null"]}}}},
    file={"url": "https://example.com/invoice.pdf"}
)
```

#### Java

**`After (2026-02-09)`**

```java title="After (2026-02-09)"
var result = client.extract(ExtractRequest.builder()
    .file(ExtractRequestFile.of(FileFromUrl.builder().url("https://example.com/invoice.pdf").build()))
    .extractor(ExtractRequestExtractor.builder().id("ex_abc123").build())
    .build());
result.getOutput().ifPresent(output ->
    System.out.println(((ExtractOutputJson) output.get()).getValue()));
```

### Listing Extract Runs

#### TypeScript

**`Before`**

```typescript title="Before"
const runs = await client.processorRun.list({ processorType: "EXTRACT", processorId: "dp_abc123" });
```

**`After`**

```typescript title="After"
const runs = await client.extractRuns.list({ extractorId: "ex_abc123" });
```

#### Python

**`Before`**

```python title="Before"
runs = client.processor_run.list(processor_type="EXTRACT", processor_id="dp_abc123")
```

**`After`**

```python title="After"
runs = client.extract_runs.list(extractor_id="ex_abc123")
```

#### Java

**`After`**

```java title="After"
var runs = client.extractRuns().list(ExtractRunsListRequest.builder()
    .extractorId("ex_abc123")
    .build());
```

### TypeScript: Typed Schemas with Zod

Define your extraction schema using Zod for fully typed output values. Pass a `z.object()` schema directly as `config.schema` — the SDK automatically converts it to the API's JSON Schema format and infers the output type:

```typescript
import { ExtendClient, extendDate, extendCurrency } from "extend-ai";
import { z } from "zod";

const client = new ExtendClient({ token: "your-api-key" });

// Extract with a typed zod schema
const result = await client.extract({
  config: {
    schema: z.object({
      invoice_number: z.string().nullable(),
      invoice_date: extendDate(),
      total: extendCurrency(),
      line_items: z.array(z.object({
        description: z.string().nullable(),
        quantity: z.number().nullable(),
        unit_price: z.number().nullable(),
      })),
    }),
  },
  file: { url: "https://example.com/invoice.pdf" }
});

// TypeScript knows the exact shape of output.value!
if (result.output) {
  const output = result.output.value;
  console.log(output.invoice_number);           // string | null
  console.log(output.invoice_date);             // string | null (ISO date)
  console.log(output.total.amount);             // number | null
  console.log(output.total.iso_4217_currency_code); // string | null
  output.line_items.forEach(item => {
    console.log(item.description, item.quantity, item.unit_price);
  });
}
```

You can also use Zod schemas with an existing extractor by passing them via `extractor.overrideConfig`:

```typescript
const result = await client.extract({
  extractor: {
    id: "ex_abc123",
    overrideConfig: {
      schema: z.object({
        invoice_number: z.string().nullable(),
        total: extendCurrency(),
      }),
    },
  },
  file: { url: "https://example.com/invoice.pdf" }
});
```

**Available custom type helpers:**

* `extendDate()` — Date fields (output is ISO format `yyyy-mm-dd`)
* `extendCurrency()` — Currency fields (output has `amount` and `iso_4217_currency_code`)
* `extendSignature()` — Signature detection fields (output has `printed_name`, `signature_date`, `is_signed`, `title_or_role`)

---

## Endpoint Changes Summary

| Old Endpoint                                | New Endpoint                     |
| ------------------------------------------- | -------------------------------- |
| `POST /processor_runs` (type: EXTRACT)      | `POST /extract_runs`             |
| `GET /processor_runs?processorType=EXTRACT` | `GET /extract_runs`              |
| `GET /processor_runs/{id}`                  | `GET /extract_runs/{id}`         |
| `DELETE /processor_runs/{id}`               | `DELETE /extract_runs/{id}`      |
| `POST /processor_runs/{id}/cancel`          | `POST /extract_runs/{id}/cancel` |

---

## Extraction Schema Changes

### Legacy "Fields" Format Removed

The legacy `fields` array format for defining extraction schemas is **no longer supported** in this API version. You must use the JSON Schema `schema` format instead.

**`Before: Legacy fields format (no longer supported)`**

```json title="Before: Legacy fields format (no longer supported)"
{
  "config": {
    "type": "EXTRACT",
    "fields": [
      { "id": "vendor_name", "name": "Vendor Name", "type": "string", "description": "..." },
      { "id": "total", "name": "Total", "type": "currency", "description": "..." }
    ]
  }
}
```

**`After: JSON Schema format (required)`**

```json title="After: JSON Schema format (required)"
{
  "config": {
    "schema": {
      "type": "object",
      "properties": {
        "vendor_name": { "type": ["string", "null"], "description": "..." },
        "total": { "type": "object", "extend:type": "currency", "description": "..." }
      }
    }
  }
}
```

See the [JSON Schema guide](/extraction/schema) for the full schema reference and examples.

### Strict Nullable Validation

In previous API versions, passing a non-nullable primitive type (e.g. `"type": "string"`) would be silently converted to its nullable form (`"type": ["string", "null"]`). Similarly, `enum` arrays without `null` would have `null` automatically appended.

**In `2026-02-09`, this is now a validation error.** You must explicitly declare nullable types in your schema. This prevents a mismatch between your schema definition and the actual API response — particularly important if you're using SDK features like Zod schema validation in the TypeScript SDK, where a `null` return for a non-nullable field would cause a runtime error.

> **Warning**
>
> **Breaking change:** Schemas that previously worked due to silent conversion will now return a `400 Bad Request`. Update your schemas before migrating.

**Primitive types** must use the nullable array form:

**`Before (silently converted)`**

```json title="Before (silently converted)"
{ "type": "string" }
```

**`After (required)`**

```json title="After (required)"
{ "type": ["string", "null"] }
```

**Enum arrays** must include `null`:

**`Before (null silently added)`**

```json title="Before (null silently added)"
{ "enum": ["active", "inactive"] }
```

**`After (required)`**

```json title="After (required)"
{ "enum": ["active", "inactive", null] }
```

This applies to all endpoints that accept an extraction schema: `POST /extract`, `POST /extract_runs`, `POST /extractors`, `POST /extractors/{id}`, and `POST /extractors/{extractorId}/versions`.

---

## Request Changes

### File Properties (All Endpoints)

| Old             | New         |
| --------------- | ----------- |
| `file.fileUrl`  | `file.url`  |
| `file.fileId`   | `file.id`   |
| `file.fileName` | `file.name` |
| `rawText`       | `file.text` |

### Creating an Extract Run

| Old                       | New                        | Notes                                                                                                        |
| ------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `processorId`             | `extractor.id`             | Nested in object                                                                                             |
| `version`                 | `extractor.version`        | Nested in object                                                                                             |
| `config` (with extractor) | `extractor.overrideConfig` | **Moved inside extractor object.** Top-level `config` is now only for inline extraction without an extractor |
| `sync: true`              | *(removed)*                | Use `POST /extract` (sync, for testing), or `createAndPoll()` / webhooks for production                      |
| `config.type: "EXTRACT"`  | *(removed)*                | Implicit from endpoint                                                                                       |
| `config.parser`           | `parseConfig`              | **Renamed.** Use `config.parseConfig` (inline) or `extractor.overrideConfig.parseConfig` (with extractor)    |

> **Warning**
>
> **Breaking change:** If you previously passed `config` alongside a `processorId` to override extractor settings, you must now pass it as `extractor.overrideConfig` inside the extractor object. The top-level `config` property is reserved for inline extraction (without an extractor).
>
> Additionally, `config.parser` has been renamed to `parseConfig` everywhere — use `config.parseConfig` for inline config or `extractor.overrideConfig.parseConfig` when overriding an extractor.

### Example: Create Request

**`Before (2025-04-21)`**

```json title="Before (2025-04-21)"
{
  "processorId": "dp_abc123",
  "version": "latest",
  "file": {
    "fileUrl": "https://example.com/invoice.pdf",
    "fileName": "invoice.pdf"
  },
  "sync": true,
  "metadata": { "customerId": "cust_123" }
}
```

**`After (2026-02-09)`**

```json title="After (2026-02-09)"
{
  "extractor": {
    "id": "ex_abc123",
    "version": "latest"
  },
  "file": {
    "url": "https://example.com/invoice.pdf",
    "name": "invoice.pdf"
  },
  "metadata": { "customerId": "cust_123" }
}
```

### Example: Overriding Extractor Config

If you previously passed `config` alongside `processorId` to override the extractor's settings, this now moves inside the `extractor` object as `overrideConfig`:

**`Before (2025-04-21)`**

```json title="Before (2025-04-21)"
{
  "processorId": "dp_abc123",
  "config": {
    "schema": { "type": "object", "properties": { "vendorName": { "type": "string" } } },
    "parser": { "type": "SIMPLE" }
  },
  "file": { "fileUrl": "https://example.com/invoice.pdf" },
  "sync": true
}
```

**`After (2026-02-09)`**

```json title="After (2026-02-09)"
{
  "extractor": {
    "id": "ex_abc123",
    "overrideConfig": {
      "schema": { "type": "object", "properties": { "vendorName": { "type": ["string", "null"] } } },
      "parseConfig": { "type": "SIMPLE" }
    }
  },
  "file": { "url": "https://example.com/invoice.pdf" }
}
```

---

## Response Changes

> **Note**
>
> **Response shape changes:** Single object responses are now returned directly (no wrapper key), and list responses use `{ "object": "list", "data": [...] }` format. See [Simplified Response Shapes](/api-reference/migrations/2026-02-09/overview#simplified-response-shapes) for details.

### Key Differences

| Old                               | New                                    |
| --------------------------------- | -------------------------------------- |
| `success: true`                   | *(removed)* — Use HTTP status codes    |
| `{ "extractRun": {...} }`         | `{...}` (object returned directly)     |
| `processorRun.processorId`        | `extractRun.extractor.id`              |
| `processorRun.processorVersionId` | `extractRun.extractorVersion.id`       |
| `processorRun.files[]`            | `extractRun.file` (single object)      |
| `processorRun.url`                | `extractRun.dashboardUrl`              |
| Optional fields (may be absent)   | Required but nullable (always present) |

### Example: Response

**`Before (2025-04-21)`**

```json title="Before (2025-04-21)"
{
  "success": true,
  "processorRun": {
    "object": "document_processor_run",
    "id": "dpr_abc123",
    "processorId": "dp_xyz789",
    "processorVersionId": "dpv_456",
    "processorName": "Invoice Extractor",
    "type": "EXTRACT",
    "status": "PROCESSED",
    "output": {
      "value": { "vendorName": "Acme Corp" },
      "metadata": { "vendorName": { "logprobsConfidence": 0.95 } }
    },
    "files": [{ "id": "file_123", "name": "invoice.pdf" }],
    "url": "https://dashboard.extend.ai/runs/dpr_abc123"
  }
}
```

**`After (2026-02-09)`**

```json title="After (2026-02-09)"
{
  "object": "extract_run",
  "id": "exr_abc123",
  "extractor": {
    "object": "extractor",
    "id": "ex_xyz789",
    "name": "Invoice Extractor"
  },
  "extractorVersion": {
    "object": "extractor_version",
    "id": "exv_456",
    "version": "1.0"
  },
  "status": "PROCESSED",
  "output": {
    "value": { "vendorName": "Acme Corp" },
    "metadata": { "vendorName": { "logprobsConfidence": 0.95 } }
  },
  "file": { "id": "file_123", "name": "invoice.pdf" },
  "dashboardUrl": "https://dashboard.extend.ai/runs/exr_abc123",
  "failureReason": null,
  "failureMessage": null,
  "reviewedOutput": null
}
```

---

## Citation Format Change

If you're using citations (bounding boxes), the `page` field structure has changed:

**`Before (2025-04-21)`**

```typescript title="Before (2025-04-21)"
const pageNumber = citation.page;  // number
```

**`After (2026-02-09)`**

```typescript title="After (2026-02-09)"
const pageNumber = citation.page.number;  // number
const pageWidth = citation.page.width;    // number (in points)
const pageHeight = citation.page.height;  // number (in points)
```

---

## SDK Method Reference

| Old Method                     | New Method                                   |
| ------------------------------ | -------------------------------------------- |
| `client.processorRun.create()` | `client.extractRuns.create()`                |
| `client.processorRun.get()`    | `client.extractRuns.retrieve()`              |
| `client.processorRun.list()`   | `client.extractRuns.list()`                  |
| `client.processorRun.delete()` | `client.extractRuns.delete()`                |
| `client.processorRun.cancel()` | `client.extractRuns.cancel()`                |
| —                              | `client.extract()` *(new sync endpoint)*     |
| —                              | `client.extractRuns.createAndPoll()` *(new)* |

---

#### Detailed Schema Changes

### ExtractRun Schema

| Property             | Old (ProcessorRun)         | New (ExtractRun)                           | Change                             |
| -------------------- | -------------------------- | ------------------------------------------ | ---------------------------------- |
| `object`             | `"document_processor_run"` | `"extract_run"`                            | Value changed                      |
| `id`                 | Required `string`          | Required `string`                          | No change                          |
| `processorId`        | Required                   | —                                          | Removed, see `extractor.id`        |
| `processorVersionId` | Required                   | —                                          | Removed, see `extractorVersion.id` |
| `processorName`      | Required                   | —                                          | Removed, see `extractor.name`      |
| `extractor`          | —                          | Required `ExtractorSummary \| null`        | New                                |
| `extractorVersion`   | —                          | Required `ExtractorVersionSummary \| null` | New                                |
| `type`               | Required `"EXTRACT"`       | —                                          | Removed (implicit)                 |
| `status`             | Required                   | Required                                   | No change                          |
| `output`             | Required                   | Required but nullable                      | Now nullable                       |
| `initialOutput`      | Optional                   | Required but nullable                      | Now required                       |
| `reviewedOutput`     | Optional                   | Required but nullable                      | Now required                       |
| `failureReason`      | Optional                   | Required but nullable                      | Now required                       |
| `failureMessage`     | Optional                   | Required but nullable                      | Now required                       |
| `metadata`           | Optional                   | Required but nullable                      | Now required                       |
| `config`             | `ExtractionConfig`         | `ExtractConfig`                            | No change                          |
| `files`              | Required `File[]`          | —                                          | Removed                            |
| `file`               | —                          | Required `FileSummary`                     | New                                |
| `parseRunId`         | —                          | Required `string \| null`                  | New                                |
| `url`                | Required                   | —                                          | Renamed                            |
| `dashboardUrl`       | —                          | Required                                   | New (replaces `url`)               |
| `usage`              | Optional                   | Required but nullable                      | Now required                       |
| `createdAt`          | —                          | Required                                   | New                                |
| `updatedAt`          | —                          | Required                                   | New                                |

### ExtractConfig Schema

| Property        | Old (ExtractionConfig) | New (ExtractConfig) | Change                   |
| --------------- | ---------------------- | ------------------- | ------------------------ |
| `type`          | Required `"EXTRACT"`   | —                   | Removed (implicit)       |
| `baseProcessor` | Optional               | Optional            | No change                |
| `schema`        | Optional               | Required            | Now required             |
| `fields`        | Optional (deprecated)  | —                   | Removed                  |
| `parser`        | Optional               | —                   | Renamed to `parseConfig` |
| `parseConfig`   | —                      | Optional            | New                      |

### ExtractRunSummary Schema (List Response)

| Property           | Old (ProcessorRunSummary) | New (ExtractRunSummary) | Change                        |
| ------------------ | ------------------------- | ----------------------- | ----------------------------- |
| `object`           | —                         | `"extract_run"`         | New                           |
| `processorId`      | Required                  | —                       | Removed, see `extractor.id`   |
| `processorName`    | Required                  | —                       | Removed, see `extractor.name` |
| `extractor`        | —                         | Required                | New                           |
| `extractorVersion` | —                         | Required                | New                           |
| `type`             | Optional                  | —                       | Removed                       |
| `file`             | —                         | Required                | New                           |
| `dashboardUrl`     | —                         | Required                | New                           |

---

## Need Help?

If you encounter any issues while migrating, please contact our support team at [support@extend.app](mailto:support@extend.app).

---

## Migration Guides

| Guide                            | Migrating From           | Migrating To                            |
| -------------------------------- | ------------------------ | --------------------------------------- |
| [Overview](./overview)           | —                        | What's new and how to upgrade           |
| [Extract Runs](./extract)        | `/processor_runs`        | `/extract_runs` + `/extract`            |
| [Classify Runs](./classify)      | `/processor_runs`        | `/classify_runs` + `/classify`          |
| [Split Runs](./split)            | `/processor_runs`        | `/split_runs` + `/split`                |
| [Parse Runs](./parse)            | `/parse`, `/parse/async` | `/parse_runs` + `/parse`                |
| [Edit Runs](./edit)              | `/edit`, `/edit/async`   | `/edit_runs` + `/edit`                  |
| [Extractors](./extractors)       | `/processors`            | `/extractors`                           |
| [Classifiers](./classifiers)     | `/processors`            | `/classifiers`                          |
| [Splitters](./splitters)         | `/processors`            | `/splitters`                            |
| [Files](./files)                 | `/files`                 | `/files` (breaking changes)             |
| [Evaluation Sets](./evaluation)  | evaluation endpoints     | Updated evaluation endpoints            |
| [Workflow Runs](./workflow-runs) | `/workflow_runs`         | `/workflow_runs` (breaking changes)     |
| [Webhooks](./webhooks)           | `processor_run.*` events | `extract_run.*`, `classify_run.*`, etc. |