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

# Editing Overview

> Editing detects and fills PDF form fields from data or natural-language instructions, then returns a completed PDF. Learn how it works, then run it in minutes.

**Editing** fills PDF form fields programmatically and returns a completed document. You give it a PDF and either natural-language `instructions` or a `schema` describing the fields and values, and Extend detects the form's fields, fills them, and returns a downloadable PDF along with the values it wrote. Use it to auto-fill applications, pre-populate documents with customer data, and generate filled forms at scale.

## Quick start

We'll fill a blank IRS Form 1040 from natural-language instructions. For this quick-start we've uploaded the file [here.](https://extend-public-files.s3.us-east-2.amazonaws.com/f1040.pdf)

![First page of IRS Form 1040](/_fern-img/1d24cfdf082d30de12aa3f3000689d486b79db45e9f73ffb3acfeaaf74b3675f.webp)

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"
```

The `/edit` endpoint takes a `file` and a `config` with `instructions` (or a `schema`).

#### Python

```python
from extend_ai import Extend

client = Extend()

result = client.edit(
    file={
        "url": "https://extend-public-files.s3.us-east-2.amazonaws.com/f1040.pdf",
    },
    config={
        "engineVersion": "1.0.0",
        "instructions": (
            "Fill the taxpayer's first name as Jordan and last name as Avery. "
            "Set the Single filing status. Leave all other fields blank."
        ),
        "advancedOptions": {"flattenPdf": True},
    },
)

print(result)
```

#### TypeScript

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

const client = new ExtendClient();

const result = await client.edit({
  file: {
    url: "https://extend-public-files.s3.us-east-2.amazonaws.com/f1040.pdf",
  },
  config: {
    engineVersion: "1.0.0",
    instructions:
      "Fill the taxpayer's first name as Jordan and last name as Avery. Set the Single filing status. Leave all other fields blank.",
    advancedOptions: { flattenPdf: true },
  },
});

console.log(result);
```

#### Java

```java
import ai.extend.ExtendClient;
import ai.extend.requests.EditRequest;
import ai.extend.types.EditConfig;
import ai.extend.types.EditAdvancedOptions;
import ai.extend.types.EditRequestFile;
import ai.extend.types.EditRun;
import ai.extend.types.FileFromUrl;

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

EditRun result = client.edit(EditRequest.builder()
    .file(EditRequestFile.of(FileFromUrl.builder()
        .url("https://extend-public-files.s3.us-east-2.amazonaws.com/f1040.pdf")
        .build()))
    .config(EditConfig.builder()
        .engineVersion("1.0.0")
        .instructions("Fill the taxpayer's first name as Jordan and last name as Avery. "
            + "Set the Single filing status. Leave all other fields blank.")
        .advancedOptions(EditAdvancedOptions.builder().flattenPdf(true).build())
        .build())
    .build());

System.out.println(result);
```

#### Go

```go
package main

import (
	"context"
	"fmt"
	"log"

	extend "github.com/extend-hq/extend-go-sdk"
	client "github.com/extend-hq/extend-go-sdk/client"
)

func main() {
	c := client.NewClient()

	result, err := c.Edit(context.TODO(), &extend.EditRequest{
		File: &extend.EditRequestFile{
			FileFromURL: &extend.FileFromURL{
				URL: "https://extend-public-files.s3.us-east-2.amazonaws.com/f1040.pdf",
			},
		},
		Config: &extend.EditConfig{
		EngineVersion: extend.String("1.0.0"),
			Instructions: extend.String("Fill the taxpayer's first name as Jordan and last name as Avery. Set the Single filing status. Leave all other fields blank."),
			AdvancedOptions: &extend.EditAdvancedOptions{
				FlattenPdf: extend.Bool(true),
			},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result)
}
```

#### cURL

```bash
curl -X POST https://api.extend.ai/edit \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Authorization: Bearer $EXTEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": {
      "url": "https://extend-public-files.s3.us-east-2.amazonaws.com/f1040.pdf"
    },
    "config": {
      "engineVersion": "1.0.0",
      "instructions": "Fill the taxpayer'\''s first name as Jordan and last name as Avery. Set the Single filing status. Leave all other fields blank.",
      "advancedOptions": { "flattenPdf": true }
    }
  }'
```

> **Tip**
>
> Want to edit your own document? [Upload it](/api-reference/endpoints/file/upload-file) first, then pass the returned file `id` instead of a `url` (reusing the same `config`).
>
> #### Python
>
> ```python
> with open("f1040.pdf", "rb") as f:
>     uploaded = client.files.upload(file=f)
>
> result = client.edit(file={"id": uploaded.id}, config=config)
> ```
>
> #### TypeScript
>
> ```typescript
> import { createReadStream } from "fs";
>
> const uploaded = await client.files.upload(createReadStream("f1040.pdf"), {});
>
> const result = await client.edit({ file: { id: uploaded.id }, config });
> ```
>
> #### Java
>
> ```java
> import ai.extend.requests.FilesUploadRequest;
> import ai.extend.types.File;
> import ai.extend.types.FileFromId;
>
> File uploaded = client.files().upload(
>     new java.io.File("f1040.pdf"),
>     FilesUploadRequest.builder().build());
>
> EditRun result = client.edit(EditRequest.builder()
>     .file(EditRequestFile.of(FileFromId.builder().id(uploaded.getId()).build()))
>     .config(config) // the EditConfig from above
>     .build());
> ```
>
> #### Go
>
> ```go
> f, err := os.Open("f1040.pdf")
> if err != nil {
> 	log.Fatal(err)
> }
> defer f.Close()
>
> uploaded, err := c.Files.Upload(context.TODO(), f, &extend.FilesUploadRequest{})
> if err != nil {
> 	log.Fatal(err)
> }
>
> result, err := c.Edit(context.TODO(), &extend.EditRequest{
> 	File:   &extend.EditRequestFile{FileFromID: &extend.FileFromID{ID: uploaded.ID}},
> 	Config: config, // the *extend.EditConfig from above
> })
> ```
>
> #### cURL
>
> ```bash
> # Upload the file to get an id
> curl -X POST https://api.extend.ai/files/upload \
>   -H "Authorization: Bearer $EXTEND_API_KEY" \
>   -H "x-extend-api-version: 2026-02-09" \
>   -F "file=@f1040.pdf"
>
> # Then edit using the returned id
> curl -X POST https://api.extend.ai/edit \
>   -H "Authorization: Bearer $EXTEND_API_KEY" \
>   -H "x-extend-api-version: 2026-02-09" \
>   -H "Content-Type: application/json" \
>   -d '{ "file": { "id": "file_xK9mLPqRtN3vS8wF5hB2cQ" }, "config": { "instructions": "Fill the form with the provided data." } }'
> ```

### Example response

After you run the code snippet above, you'll see a response like this. Extend detects the form's fields, fills the ones your instructions describe, and returns an `output` with the completed PDF in `editedFile` and the written values in `filledValues`.

```json
{
  "object": "edit_run",
  "id": "edr_xK9mLPqRtN3vS8wF5hB2cQ",
  "status": "PROCESSED",
  "output": {
    "editedFile": {
      "id": "file_Ab3cDE45Fg6hIj7KlM8nO",
      "presignedUrl": "https://extend-files.s3.amazonaws.com/..."
    },
    "filledValues": {
      "Your first name and middle initial": "Jordan",
      "Last name": "Avery",
      "filing_status_single": true
    }
  },
  "metrics": { "processingTimeMs": 1234, "pageCount": 2, "fieldCount": 3 },
  "usage": { "credits": 1 }
}
```

### Key fields

| Field                            | What it contains                                                                 |
| -------------------------------- | -------------------------------------------------------------------------------- |
| `output.editedFile.id`           | The Extend file `id` of the completed PDF, reusable as input to other endpoints. |
| `output.editedFile.presignedUrl` | A download URL for the completed PDF. Expires after 15 minutes.                  |
| `output.filledValues`            | The values written into the form, keyed by schema property name.                 |
| `status`                         | `PROCESSING`, `PROCESSED`, or `FAILED`.                                          |

For full request/response details, see the [Create Edit Run API reference](/api-reference/endpoints/edit/create-edit-run).

### Use the output

Read the download link off `output.editedFile`, and inspect `output.filledValues` to see what was written.

#### Python

```python
edited = result.output.edited_file
print("Download:", edited.presigned_url)  # expires after 15 minutes

for field, value in (result.output.filled_values or {}).items():
    print(f"{field}: {value}")
```

#### TypeScript

```typescript
const edited = result.output?.editedFile;
console.log("Download:", edited?.presignedUrl); // expires after 15 minutes

for (const [field, value] of Object.entries(result.output?.filledValues ?? {})) {
  console.log(`${field}: ${value}`);
}
```

#### Java

```java
var output = result.getOutput().get();
System.out.println("Download: " + output.getEditedFile().getPresignedUrl()); // expires after 15 minutes

output.getFilledValues().ifPresent(values ->
    values.forEach((field, value) -> System.out.println(field + ": " + value)));
```

#### Go

```go
edited := result.Output.EditedFile
fmt.Println("Download:", edited.PresignedURL) // expires after 15 minutes

for field, value := range result.Output.FilledValues {
	fmt.Printf("%s: %v\n", field, value)
}
```

For the full response shape and status values, see [Response Format](/editing/response-format).

## Sync vs async

The example above calls the synchronous `/edit` endpoint. We also have an asynchronous `/edit_runs` endpoint that should be used for large files and high volume use cases.

See [Async Processing](/general/async-processing) for the full comparison, polling options, and webhook setup.

## Reuse a saved template

If you've saved an [edit template](/api-reference/endpoints/edit/get-edit-template) — a source file paired with a default `config` — you can pass its ID as `templateId` in place of `file`:

```json
{ "templateId": "edt_xK9mLPqRtN3vS8wF5hB2cQ" }
```

Extend runs the edit against the template's saved file, using the template's saved schema, instructions, engine version, and advanced options as defaults. Any `config` values you also include in the request take precedence over the template's saved values, so you can override just what's different for a given run:

```json
{
  "templateId": "edt_xK9mLPqRtN3vS8wF5hB2cQ",
  "config": { "instructions": "Use today's date for the signature date field." }
}
```

Exactly one of `file` or `templateId` must be provided — a request either supplies a file directly or references a template, not both. If the template ID doesn't exist the request fails with a `404`; if it belongs to another workspace or organization it fails with a `403`.

## Configuration

The quick start sends `file` and `config.instructions`. To control how the form is filled, pass more options inside `config`. Here are the most commonly used ones; for the full reference, see [Configuration](/editing/configuration).

### Instructions

Natural-language guidance describing the values to fill and any special handling. The fastest way to start — Extend detects the form's fields and fills the ones you describe.

```json
{ "config": { "instructions": "Fill the applicant's name and today's date. Leave optional fields blank." } }
```

### Schema

A JSON Schema that pins each field's PDF type, value, and position using `extend_edit:*` keywords. Use it for exact, repeatable control over what gets filled and where.

```json
{
  "config": {
    "schema": {
      "type": "object",
      "properties": {
        "applicant_name": {
          "type": ["string", "null"],
          "extend_edit:field_type": "text",
          "extend_edit:value": "Jane Smith"
        }
      }
    }
  }
}
```

[Full schema reference →](/editing/configuration#schema)

### Flatten the PDF

Flatten the document after editing so the filled fields become non-editable. Use it when generating final documents.

```json
{ "config": { "advancedOptions": { "flattenPdf": true } } }
```

> **Note**
>
> Don't have a schema yet? [Detect Form](/editing/detect-form) detects a form's fields and returns a starter schema with positions annotated. For every config option, see the [Configuration](/editing/configuration) reference.

---

## Tutorial video

## Next steps

#### [Configuration](/editing/configuration)

Instructions, schema, field types, and advanced options.

#### [Detect Form](/editing/detect-form)

Detect a form's fields and get a starter schema.

#### [Response Format](/editing/response-format)

The edit run output, filled values, and status values.

#### [Best Practices](/editing/best-practices)

Schemas, flattening, polling, and reliable production runs.