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

# Detect Form

> Detect fields in a PDF form and generate a ready-to-use edit schema with annotated positions, optional schema mapping, and conditional validation rules.

**Detect Form** detects the fields in a PDF form and returns a `form_detection_run` whose `output.schema` you can pass straight to `/edit` or `/edit_runs`. The returned schema has field positions annotated, so you don't have to write `extend_edit:bbox` coordinates by hand. Optionally, supply your own schema and Extend maps it onto the detected fields. Set `advancedOptions.conditionalGenerationEnabled` to `true` to also generate JSON Schema validation rules from requirements stated in the form.

Reach for it when you:

* Need a starter schema for a new PDF form.
* Want to map an existing schema onto a vendor-specific layout.
* Need annotated field locations before running edits at scale.
* Want Extend to generate [conditional form validation rules](/editing/configuration#conditional-form-editing) from the form's content.

> **Note**
>
> Use `POST /detect_form` for synchronous testing. For production workloads, use `POST /form_detection_runs` and receive the result by webhook or poll `GET /form_detection_runs/{id}` to avoid request timeout issues.

## Quick start

We'll detect the fields in a PDF form. Grab a key from the [Developers](https://dashboard.extend.ai/developers) page and store it as the `EXTEND_API_KEY` environment variable. If you're using an SDK, see the [installation instructions](/sdks).

```bash
export EXTEND_API_KEY="your_api_key_here"
```

#### Python

```python
from extend_ai import Extend

client = Extend()

run = client.detect_form(
    file={"url": "https://example.com/form.pdf"},
    config={
        "engineVersion": "1.0.0",
        "instructions": "Detect the form fields and use human-readable field names.",
        "advancedOptions": {"radioEnumsEnabled": True},
    },
)

print(run.output.schema)
```

#### TypeScript

```typescript
import { ExtendClient } from "extend-ai";

const client = new ExtendClient();

const run = await client.detectForm({
  file: { url: "https://example.com/form.pdf" },
  config: {
    engineVersion: "1.0.0",
    instructions: "Detect the form fields and use human-readable field names.",
    advancedOptions: { radioEnumsEnabled: true },
  },
});

console.log(run.output?.schema);
```

#### Java

```java
import ai.extend.ExtendClient;
import ai.extend.requests.DetectFormRequest;
import ai.extend.types.EditSchemaGenerationConfig;
import ai.extend.types.DetectFormRequestFile;
import ai.extend.types.FileFromUrl;
import ai.extend.types.FormDetectionRun;

ExtendClient client = ExtendClient.builder().build();

FormDetectionRun run = client.detectForm(
    DetectFormRequest.builder()
        .file(DetectFormRequestFile.of(FileFromUrl.builder()
            .url("https://example.com/form.pdf")
            .build()))
        .config(EditSchemaGenerationConfig.builder()
            .engineVersion("1.0.0")
            .instructions("Detect the form fields and use human-readable field names.")
            .build())
        .build());

System.out.println(run.getOutput().get().getSchema());
```

#### Go

```go
run, err := c.DetectForm(context.TODO(), &extend.DetectFormRequest{
	File: &extend.DetectFormRequestFile{
		FileFromURL: &extend.FileFromURL{URL: "https://example.com/form.pdf"},
	},
	Config: &extend.EditSchemaGenerationConfig{
		EngineVersion: extend.String("1.0.0"),
		Instructions: extend.String("Detect the form fields and use human-readable field names."),
	},
})
if err != nil {
	log.Fatal(err)
}

fmt.Println(run.Output.Schema)
```

#### cURL

```bash
curl -X POST https://api.extend.ai/detect_form \
  -H "Authorization: Bearer $EXTEND_API_KEY" \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Content-Type: application/json" \
  -d '{
    "file": {
      "url": "https://example.com/form.pdf"
    },
    "config": {
      "engineVersion": "1.0.0",
      "instructions": "Detect the form fields and use human-readable field names.",
      "advancedOptions": { "radioEnumsEnabled": true }
    }
  }'
```

## Response

The response is a `form_detection_run`. A successful synchronous request returns after the run reaches `PROCESSED`; the asynchronous endpoint usually returns it with `status: "PROCESSING"`.

| Field                    | Type                   | Description                                                                                                                                                                                                                                         |
| ------------------------ | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `object`                 | `"form_detection_run"` | The resource type.                                                                                                                                                                                                                                  |
| `id`                     | string                 | The form detection run ID. Use it with `GET /form_detection_runs/{id}`.                                                                                                                                                                             |
| `status`                 | string                 | `PROCESSING`, `PROCESSED`, or `FAILED`.                                                                                                                                                                                                             |
| `config`                 | object                 | The full form detection configuration, including defaults that were applied.                                                                                                                                                                        |
| `config.engineVersion`   | string                 | The resolved exact Edit engine version, even when the request used `"latest"`.                                                                                                                                                                      |
| `output`                 | object \| null         | Present when processing succeeds. Contains `schema`, `annotatedSchema`, and `mappingResult`.                                                                                                                                                        |
| `output.schema`          | object                 | The final generated schema after mapping. If no `inputSchema` was provided, this is the same as `output.annotatedSchema`. Keep `extend_edit:source_acroform` and `extend:descriptions` when adding values and using this schema in an Edit request. |
| `output.annotatedSchema` | object \| null         | The original schema detected from the file, annotated with field locations.                                                                                                                                                                         |
| `output.mappingResult`   | object \| null         | Present when you provide an `inputSchema`. Contains `matches`, `unmatchedInputPaths`, and `unusedFormFieldKeys`.                                                                                                                                    |
| `metrics`                | object \| null         | Page, field, and processing-time metrics. Present when processing succeeds.                                                                                                                                                                         |
| `usage`                  | object \| null         | Credits consumed by the run when usage data is available.                                                                                                                                                                                           |

## Asynchronous processing

For production workloads, create a form detection run and receive its terminal state by webhook or poll it by ID:

```bash
curl -X POST https://api.extend.ai/form_detection_runs \
  -H "Authorization: Bearer $EXTEND_API_KEY" \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Content-Type: application/json" \
  -d '{
    "file": { "url": "https://example.com/form.pdf" },
    "config": {
      "engineVersion": "1.0.0",
      "instructions": "Detect the form fields and use human-readable field names.",
      "advancedOptions": { "conditionalGenerationEnabled": true }
    }
  }'

curl https://api.extend.ai/form_detection_runs/sgr_xK9mLPqRtN3vS8wF5hB2cQ \
  -H "Authorization: Bearer $EXTEND_API_KEY" \
  -H "x-extend-api-version: 2026-02-09"
```

Subscribe a webhook endpoint to `form_detection_run.processed` and `form_detection_run.failed` to receive the completed `form_detection_run` payload without polling. These are global webhook events and are emitted only for runs created through the API. See the [processed](/api-reference/webhook-events/edit/form-detection-run-processed-webhook) and [failed](/api-reference/webhook-events/edit/form-detection-run-failed-webhook) event references for their payloads.

Alternatively, poll until `status` is `PROCESSED` or `FAILED`. See [Async Processing](/general/async-processing) for retry, polling, and webhook guidance.

## Request configuration

The `config` object is shared by the [sync](/api-reference/endpoints/edit/detect-form) and [async](/api-reference/endpoints/edit/detect-form-async) form detection endpoints:

| Field                                          | Type    | Description                                                                                                                                                                                                              |
| ---------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `engineVersion`                                | string  | The Edit engine version to use for form detection. Pass an exact version for reproducible results, or `"latest"` for the latest stable version. Defaults to `"1.0.0"`. The response contains the resolved exact version. |
| `inputSchema`                                  | object  | An existing edit schema to map onto the detected form fields. When provided, the response includes a `mappingResult`.                                                                                                    |
| `instructions`                                 | string  | Custom instructions for field detection and naming.                                                                                                                                                                      |
| `advancedOptions.tableParsingEnabled`          | boolean | Parse table regions as arrays of objects. Defaults to `false`.                                                                                                                                                           |
| `advancedOptions.radioEnumsEnabled`            | boolean | Represent radio groups as enums so only one option can be selected. Defaults to `true` for `1.0.0` and `1.0.0-beta`, and `false` for `0.0.1`.                                                                            |
| `advancedOptions.nativeFieldsOnly`             | boolean | Only use native AcroForm fields from the PDF and skip object detection. Defaults to `false`.                                                                                                                             |
| `advancedOptions.conditionalGenerationEnabled` | boolean | Generate root-level JSON Schema conditional validation rules from requirements explicitly stated in the form. Defaults to `false`.                                                                                       |

## Generate conditional validation rules

When `advancedOptions.conditionalGenerationEnabled` is `true`, Extend reads the form content together with the detected field schema and adds validation rules that the form explicitly asks for. For example, a form that says “If Yes, provide an explanation” can produce an `if` / `then` rule that requires the explanation field only when the answer is `true`.

Generated rules are added to `output.schema` using supported root-level JSON Schema keywords: `dependentRequired`, `if` / `then` / `else`, `allOf`, `oneOf`, `anyOf`, and `not`. Extend only generates rules for fields present in the detected schema; it does not infer requirements from layout, proximity, or general knowledge about the form.

To learn how these keywords work, see [Conditional schema validation](https://json-schema.org/understanding-json-schema/reference/conditionals) in the JSON Schema documentation.

These conditionals act as **validation rules for the form data**. When you pass the generated schema to `/edit` or `/edit_runs`, Extend checks generated field values against the rules and can retry generated values that do not satisfy them. If the values still fail validation, the Edit run fails with `SCHEMA_VALIDATION_ERROR`—in the HTTP error body's `code` for `/edit`, or in the run's `failureReason` for `/edit_runs`. They do not add interactive UI behavior such as showing or hiding fields in a browser.

> **Note**
>
> Conditional generation is model-based. Review the generated rules before using the schema in production, especially for high-stakes forms.

## A common workflow

The generated schema uses the same format as edit runs, including support for [root-level JSON Schema conditionals](/editing/configuration#conditional-form-editing). A common workflow is:

1. Generate a schema from the PDF form with `conditionalGenerationEnabled: true`.
2. Review and, if needed, refine the generated validation rules.
3. Use the schema with `/edit` or `/edit_runs`.

For the exact request and response schemas, see [Detect Form (Sync)](/api-reference/endpoints/edit/detect-form), [Detect Form (Async)](/api-reference/endpoints/edit/detect-form-async), and [Get Form Detection Run](/api-reference/endpoints/edit/get-form-detection-run).