> ## Documentation Index
> Fetch the complete documentation index at: https://agents.nanonets.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Files and structured output

> Attach files to a run, and shape the response into a JSON schema you control.

`POST /v1/agents/{agent_id}/run` takes two flavours of body. Use JSON for text-only runs;
use `multipart/form-data` whenever you need to attach files.

## Attach one or more files

Send the request as `multipart/form-data` and add a `files` field per attachment.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://agents.nanonets.com/api/v1/agents/$AGENT_ID/run \
    -H "Authorization: Bearer $NANONETS_API_KEY" \
    -F "query=Extract line items from this invoice." \
    -F "files=@invoice.pdf" \
    -F "files=@invoice-back.pdf"
  ```

  ```python Python theme={null}
  import os, requests

  files = [
      ("files", ("invoice.pdf", open("invoice.pdf", "rb"), "application/pdf")),
      ("files", ("invoice-back.pdf", open("invoice-back.pdf", "rb"), "application/pdf")),
  ]
  resp = requests.post(
      f"https://agents.nanonets.com/api/v1/agents/{os.environ['AGENT_ID']}/run",
      headers={"Authorization": f"Bearer {os.environ['NANONETS_API_KEY']}"},
      data={"query": "Extract line items from this invoice."},
      files=files,
      timeout=60,
  )
  resp.raise_for_status()
  print(resp.json()["task_id"])
  ```

  ```javascript Node.js theme={null}
  import fs from "node:fs";

  const form = new FormData();
  form.append("query", "Extract line items from this invoice.");
  form.append("files", new Blob([fs.readFileSync("invoice.pdf")]), "invoice.pdf");
  form.append("files", new Blob([fs.readFileSync("invoice-back.pdf")]), "invoice-back.pdf");

  const res = await fetch(
    `https://agents.nanonets.com/api/v1/agents/${process.env.AGENT_ID}/run`,
    {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.NANONETS_API_KEY}` },
      body: form,
    },
  );
  const { task_id } = await res.json();
  ```
</CodeGroup>

<Note>
  Either `query` or at least one file is required. You can send files with no text query
  if the agent's system prompt fully covers what to do with them.
</Note>

## Shape the output with `output_config`

By default, the agent's `extracted_data` follows the key definitions configured in the
dashboard. To override that per-task, send an `output_config` object:

```json theme={null}
{
  "output_config": {
    "output_schema": "{\"line_items\": [{\"sku\": \"string\", \"qty\": \"number\"}]}",
    "instructions": "Group multi-line descriptions into a single SKU."
  }
}
```

Both fields are optional individually:

* **`output_schema`**: a JSON or natural-language schema describing the desired shape.
  Strings, numbers, arrays, and nested objects are all supported. This **overrides** any
  dashboard-defined keys for this task.
* **`instructions`**: free-text guidance appended to the LLM's output-format prompt.
  Use this for hints that don't fit cleanly in the schema ("dates as `YYYY-MM-DD`",
  "skip header rows", etc.).

### JSON body: text + schema

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://agents.nanonets.com/api/v1/agents/$AGENT_ID/run \
    -H "Authorization: Bearer $NANONETS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "Extract line items from this purchase order.",
      "output_config": {
        "output_schema": "{\"line_items\": [{\"sku\": \"string\", \"qty\": \"number\"}]}",
        "instructions": "Group multi-line descriptions into a single SKU."
      }
    }'
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.post(
      f"https://agents.nanonets.com/api/v1/agents/{os.environ['AGENT_ID']}/run",
      headers={"Authorization": f"Bearer {os.environ['NANONETS_API_KEY']}"},
      json={
          "query": "Extract line items from this purchase order.",
          "output_config": {
              "output_schema": '{"line_items": [{"sku": "string", "qty": "number"}]}',
              "instructions": "Group multi-line descriptions into a single SKU.",
          },
      },
  )
  ```
</CodeGroup>

### Multipart body: files + schema

When sending files, `output_config` is a **string field** carrying the JSON payload:

```bash theme={null}
curl -X POST https://agents.nanonets.com/api/v1/agents/$AGENT_ID/run \
  -H "Authorization: Bearer $NANONETS_API_KEY" \
  -F "query=Extract line items from this invoice." \
  -F "files=@invoice.pdf" \
  -F 'output_config={"output_schema":"{\"line_items\":[{\"sku\":\"string\",\"qty\":\"number\"}]}"}'
```

<Warning>
  The multipart `output_config` value must be a JSON-encoded string. JSON inside
  multipart is a string field by convention; the server parses it after upload.
</Warning>

## Reading the structured result

When the task completes, the structured payload is in the `summary` response under
`extracted_data`:

```bash theme={null}
curl https://agents.nanonets.com/api/v1/tasks/$TASK_ID/summary \
  -H "Authorization: Bearer $NANONETS_API_KEY"
```

```json Response theme={null}
{
  "task_id": "f1c2a3b4-...",
  "status": "completed",
  "extracted_data": {
    "line_items": [
      { "sku": "ABC-001", "qty": 12 },
      { "sku": "ABC-002", "qty": 4 }
    ]
  }
}
```

Use `/result` instead of `/summary` if you need the full reasoning trail (every tool
call the agent made along the way). It's useful during development, overkill in production.
