> ## 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.

# Migrating to API Version 2026-02-09

> Everything you need to upgrade your integration to the latest Extend API

## Welcome to the New API

The `2026-02-09` API version is a significant improvement designed to make your integration cleaner, more type-safe, and easier to maintain. We've listened to developer feedback and restructured the API around clearer resource types and better SDK support.

## Why Migrate?

Here's what you'll get with the new API version:

### Dedicated Endpoints with Typed Responses

The unified `/processors` and `/processor_runs` endpoints have been split into dedicated endpoints for each resource type:

| Resource       | Old Endpoints                     | New Endpoints                                   |
| -------------- | --------------------------------- | ----------------------------------------------- |
| Extraction     | `/processors` + `/processor_runs` | `/extractors` + `/extract_runs` + `/extract`    |
| Classification | `/processors` + `/processor_runs` | `/classifiers` + `/classify_runs` + `/classify` |
| Splitting      | `/processors` + `/processor_runs` | `/splitters` + `/split_runs` + `/split`         |

This means **fully typed SDK responses**—no more checking `type` fields or casting outputs:

```typescript title="Before"
const run = await client.processorRun.create({
  processorId: "dp_abc123",
  file: { fileUrl: "https://..." }
  sync: true,
});
if (run.processorRun.type === "EXTRACT") {
  const output = run.processorRun.output as ExtractOutput; // Manual cast
}
```

```typescript title="After"
const result = await client.extract({
  extractor: { id: "ex_abc123" },
  file: { url: "https://..." }
});
console.log(result.output?.value); // Typed as ExtractOutput
```

**TypeScript bonus:** Define schemas with Zod for fully typed output values:

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

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

const result = await client.extract({
  config: {
    schema: z.object({
      invoice_number: z.string().nullable(),
      invoice_date: extendDate(),
      total: extendCurrency(),
    }),
  },
  file: { url: "https://..." }
});

// TypeScript knows the exact shape!
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
```

```python title="Before"
run = client.processor_run.create(
    processor_id="dp_abc123",
    file={"file_url": "https://..."}
)
if run.processor_run.type == "EXTRACT":
    output = run.processor_run.output  # Untyped
```

```python title="After"
result = client.extract(
    extractor={"id": "ex_abc123"},
    file={"url": "https://..."}
)
print(result.output.value)  # Typed as ExtractOutput
```

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

### Run Without Pre-Creating a Resource

Previously, you had to create and manage an extractor, classifier, or splitter resource before running. Now you can pass your config inline—perfect for managing schemas entirely in code.

```typescript title="Before: Required a processor resource"
const processor = await client.processor.create({
  name: "Invoice Extractor",
  type: "EXTRACT",
  config: { 
    type: "EXTRACT", 
    schema: { type: "object", properties: { vendor: { type: "string" } } }
  }
});
const run = await client.processorRun.create({
  processorId: processor.processor.id,
  file: { fileUrl: "https://..." }
});
```

```typescript title="After: Config inline, no resource needed"
const run = await client.extractRuns.create({
  config: { 
    schema: { type: "object", properties: { vendor: { type: "string" } } }
  },
  file: { url: "https://..." }
});
```

```python title="After: Config inline, no resource needed"
run = client.extract_runs.create(
    config={"schema": {"type": "object", "properties": {"vendor": {"type": "string"}}}},
    file={"url": "https://..."}
)
```

```java title="After: Config inline, no resource needed"
var run = client.extractRuns().create(ExtractRunsCreateRequest.builder()
    .file(ExtractRunsCreateRequestFile.of(FileFromUrl.builder().url("https://...").build()))
    .config(ExtractConfigJson.builder()
        .schema(Map.of("type", "object", "properties", Map.of("vendor", Map.of("type", "string"))))
        .build())
    .build());
```

### Synchronous Endpoints

New synchronous endpoints let you process a file and get the result in a single request—no polling or webhooks needed. These are ideal for onboarding, testing, and low-volume use cases:

| Sync Endpoint           | Async Equivalent      |
| ----------------------- | --------------------- |
| `POST /extract`         | `POST /extract_runs`  |
| `POST /classify`        | `POST /classify_runs` |
| `POST /split`           | `POST /split_runs`    |
| `POST /parse` (updated) | `POST /parse_runs`    |
| `POST /edit` (updated)  | `POST /edit_runs`     |

<br />

```typescript
// Sync — returns the completed result directly
const result = await client.extract({
  extractor: { id: "ex_abc123" },
  file: { url: "https://..." }
});
console.log(result.output?.value);
```

```python
# Sync — returns the completed result directly
result = client.extract(
    extractor={"id": "ex_abc123"},
    file={"url": "https://..."}
)
print(result.output.value)
```

```bash
curl -X POST https://api.extend.ai/extract \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Content-Type: application/json" \
  -d '{"extractor": {"id": "ex_abc123"}, "file": {"url": "https://..."}}'
```

**Sync endpoints are for testing and onboarding only.** For production workloads, we recommend using the async `*_runs` endpoints with [webhooks](/webhooks/configuration) or [polling](/general/async-processing), as they provide better reliability for large files and avoid timeout issues.

### Polling Helpers

The async `*_runs` endpoints are the recommended approach for production. The SDK provides `createAndPoll` / `create_and_poll` methods that handle polling with exponential backoff:

```typescript
const result = await client.extractRuns.createAndPoll({
  extractor: { id: "ex_abc123" },
  file: { url: "https://..." }
});
// Returns when status is PROCESSED or FAILED
console.log(result.output?.value);
```

```python
result = client.extract_runs.create_and_poll(
    extractor={"id": "ex_abc123"},
    file={"url": "https://..."}
)
# Returns when status is PROCESSED or FAILED
print(result.output.value)
```

```java
var result = client.extractRuns().createAndPoll(ExtractRunsCreateRequest.builder()
    .file(ExtractRunsCreateRequestFile.of(FileFromUrl.builder().url("https://...").build()))
    .extractor(ExtractRunsCreateRequestExtractor.builder().id("ex_abc123").build())
    .build());
// Returns when status is PROCESSED or FAILED
result.getOutput().ifPresent(output ->
    System.out.println(((ExtractOutputJson) output.get()).getValue()));
```

Available for all run types: `extractRuns`, `classifyRuns`, `splitRuns`, `parseRuns`, `editRuns`, `workflowRuns`.

**Workflow runs can take a long time.** Complex workflows may run for hours. For workflows, consider using webhooks instead of polling unless you know the workflow will complete quickly.

### Batch Endpoints

For high-volume use cases, dedicated batch endpoints process many files in a single request. Each returns a `batchId` you can use to track progress and fetch individual run results.

| Batch Endpoint              | Description                              | Tracking                                                      |
| --------------------------- | ---------------------------------------- | ------------------------------------------------------------- |
| `POST /parse_runs/batch`    | Batch parse multiple files               | `GET /batch_runs/{id}` + `batch_parse_run.*` webhooks         |
| `POST /extract_runs/batch`  | Batch extract from multiple files        | `GET /batch_runs/{id}` + `batch_processor_run.*` webhooks     |
| `POST /classify_runs/batch` | Batch classify multiple files            | `GET /batch_runs/{id}` + `batch_processor_run.*` webhooks     |
| `POST /split_runs/batch`    | Batch split multiple files               | `GET /batch_runs/{id}` + `batch_processor_run.*` webhooks     |
| `POST /workflow_runs/batch` | Batch run a workflow over multiple files | `GET /workflow_runs?batchId={id}` + `workflow_run.*` webhooks |

To retrieve individual run results from a batch, use the corresponding list endpoint filtered by `batchId` (e.g., `GET /extract_runs?batchId={id}`).

`GET /batch_runs/{id}` does **not** apply to workflow batches. Workflow batches are tracked via the workflow run list endpoint and `workflow_run.*` webhook events, which include the `batchId` in their payload.

### Simplified Response Shapes

API responses are now cleaner and more consistent:

**Single object responses are unwrapped.** Instead of `{ "extractRun": { ... }, "success": true }`, the API now returns the object directly:

```json title="Before"
{
  "success": true,
  "processorRun": {
    "id": "dpr_abc123",
    "status": "PROCESSED",
    "output": { ... }
  }
}
```

```json title="After"
{
  "id": "exr_abc123",
  "status": "PROCESSED",
  "output": { ... }
}
```

**List responses use a standardized format.** All list endpoints now return `{ "object": "list", "data": [...] }`:

```json title="Before"
{
  "success": true,
  "processorRuns": [ ... ],
  "nextPageToken": "..."
}
```

```json title="After"
{
  "object": "list",
  "data": [ ... ],
  "nextPageToken": "..."
}
```

**SDK users:** These changes are handled automatically by the SDK. You'll access results directly (e.g., `result.output` instead of `result.extractRun.output`).

### SDK Webhook Helpers

For event-driven processing, the SDK includes utilities for verifying signatures and parsing events with type-safe payloads:

```typescript
const event = client.webhooks.verifyAndParse(body, headers, secret);

if (event.eventType === "extract_run.processed") {
  console.log(event.payload.output); // Typed as ExtractRun
}
```

See the [Webhooks Migration guide](./webhooks) for full details and examples in all languages.

***

## How to Upgrade

### Step 1: Update to the New API Version

```bash
npm install extend-ai@latest
# or
yarn add extend-ai@latest
```

The latest SDK automatically uses the `2026-02-09` API version.

```bash
pip install extend-ai --upgrade
```

The latest SDK automatically uses the `2026-02-09` API version.

```xml
<dependency>
    <groupId>ai.extend</groupId>
    <artifactId>extend-java</artifactId>
    <version>LATEST</version>
</dependency>
```

The latest SDK automatically uses the `2026-02-09` API version.

Set the `x-extend-api-version` header in your requests:

```bash
curl -X POST https://api.extend.ai/extract_runs \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
```

### Step 2: Migrate Endpoint by Endpoint

You don't have to migrate everything at once. Start with the endpoints you use most and work through the detailed guides:

**Processing Runs:**

* [Extract Runs Migration](./extract) — `/processor_runs` → `/extract_runs` (+ new sync `/extract`)
* [Classify Runs Migration](./classify) — `/processor_runs` → `/classify_runs` (+ new sync `/classify`)
* [Split Runs Migration](./split) — `/processor_runs` → `/split_runs` (+ new sync `/split`)
* [Parse Runs Migration](./parse) — `/parse_runs` (+ sync `/parse`)
* [Edit Runs Migration](./edit) — `/edit_runs` (+ sync `/edit`)

**Resource Management:**

* [Extractors Migration](./extractors) — `/processors` → `/extractors`
* [Classifiers Migration](./classifiers) — `/processors` → `/classifiers`
* [Splitters Migration](./splitters) — `/processors` → `/splitters`

**Other Endpoints:**

* [Files Migration](./files) — Changes to `/files` endpoints
* [Evaluation Sets Migration](./evaluation) — Changes to evaluation sets and evaluation set runs
* [Workflow Runs Migration](./workflow-runs) — Breaking changes to `/workflow_runs` request/response shapes

### Step 3: Migrate Webhooks

If you use webhooks, follow the [Webhooks Migration](./webhooks) guide to update your webhook handlers. This includes:

* New event type names (e.g., `processor_run.processed` → `extract_run.processed`)
* New payload structures with typed responses
* New SDK helpers for signature verification
* Webhook endpoints can now be managed via API (`POST /webhook_endpoints`, `POST /webhook_subscriptions`) in addition to the dashboard

***

## Need Help?

* **Questions?** Contact our support team at [support@extend.app](mailto:support@extend.app)
* **Found a bug?** Let us know and we'll fix it ASAP
* **Feedback on the new API?** We'd love to hear it—reach out on Slack!

***

## Migration Guides

Jump to the specific guide for the endpoints you use:

| Guide                            | Migrating From           | Migrating To                            |
| -------------------------------- | ------------------------ | --------------------------------------- |
| [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. |