---
name: parsewise
description: Drive the Parsewise document intelligence API to extract structured data from PDFs, Office files, and images, with page-level citations back to the source documents.
when_to_use: |
  Use when the user mentions Parsewise or api.parsewise.ai, or wants to
  extract structured data from documents (PDF, DOCX, XLSX, PPTX, images),
  set up a Parsewise project, upload source documents, define extraction
  agents, launch extraction, poll pipeline status, or read resolved
  results with citations. Also use when working with code that calls the
  Parsewise API (X-API-Key header, /api/v1/projects/,
  /api/v1/projects/{id}/agents/, /api/v1/projects/{id}/results/,
  /api/v1/schema/).
---

# Parsewise API — Instructions for Coding Agents

This document orients AI coding agents (Claude Code, Cursor, Codex, Gemini, and similar) on how to drive the Parsewise platform through its HTTP API.

When in doubt, fetch the OpenAPI schema — it is the authoritative contract. Everything below is orientation and best-practice guidance on top of that schema.

## What Parsewise is

Parsewise is a document intelligence platform. You upload source documents into a project, define agents that describe what to extract from those documents, launch extraction, and then read back structured results with citations back to the source pages. The HTTP API exposes the same workflow the product uses end-to-end, so anything a user can do in the UI you can do programmatically.

## Core concepts

- **Project** — a container for documents, agents, and results. Scope one project per subject area (e.g. "Q4 leases").
- **Document** — a source file belonging to a project (PDF, Word, PowerPoint, Excel, image, or email .eml). Parsewise parses each document on upload, extracting its text and layout so agents can read it. When an `.eml` is uploaded, its attachments are also expanded into their own Documents within the same project.
- **Agent** — a reusable definition of *what* to extract and *how*. One agent produces one column of values across the project's documents.
- **Dimension** — an optional attachment to an agent that breaks its output into multiple values, one per dimension instance (e.g. one per clause, one per party, one per region).
- **Result** — the structured output of running agents against documents, including citations back to the source pages the values came from.

## Where the schema lives

The OpenAPI schema at `https://api.parsewise.ai/api/v1/schema/` is the authoritative contract for request/response shapes, field names, and enum values. It is public and does not require an API key. When anything in this document disagrees with the schema, the schema wins. Fetch it and consult it whenever you need exact shapes.

The schema endpoint returns YAML by default. Append `?format=json` (`https://api.parsewise.ai/api/v1/schema/?format=json`) for JSON.

## Where to manage API keys

Create, rotate, and revoke API keys on the Developer page in the Parsewise app at `https://app.parsewise.ai/developer`.


## Authentication

Every request to the v1 API must include your API key in the `X-API-Key` header. Keys start with the `pw_live_` prefix and are scoped to a single organisation. Treat keys like passwords: keep them in a secret manager, never commit them, and never embed them in client-side code.


## The usual flow

End-to-end extraction is six steps. Each step produces the IDs the next step needs, so do them in order. Fetch the schema for exact endpoints, request bodies, and response shapes — the description below covers the sequence and the why.

All requests carry the API key in the `X-API-Key` header, for example `X-API-Key: $PARSEWISE_API_KEY`.

1. **Create a project.** Projects are containers for everything else. Scope one project per subject area.
2. **Upload source documents.** Uploads are multipart; Parsewise parses them asynchronously. You don't need to wait for parsing to finish before launching — if parsing is still in flight when you launch, the run is queued and fires automatically once parsing completes. If no parsing is in flight, the launch pipeline itself parses any outstanding documents before extracting.
3. **Define agents.** Create one agent per column of data you want. Give each agent a short, descriptive name and an extraction task that specifies the value, its format, and any disambiguation rules — see the best practices below. See "How many values an agent produces" for when to attach a dimension.
4. **Launch extraction.** Launching starts the project's extraction pipeline. It takes no body and returns `202 Accepted` immediately; the actual work happens in the background.
5. **Poll for progress.** Each agent reports progress across four stages: parsing, extraction, web search, and resolution. The backend is still working while `pipeline_running=true`; treat a run as "done and successful" only when `pipeline_running=false` *and* every agent's `extraction_status` is `Processed` — see the FAQ for why `pipeline_running=false` alone is not enough. Poll with exponential backoff (e.g. 2s, 4s, 8s, capped at ~30s); do not hammer the endpoint on a tight loop. Do not issue a new launch until the previous pipeline has finished — a second launch issued mid-run cancels the in-flight run and starts a fresh one, so any partial work from the previous run is thrown away. (The one exception is a launch issued while documents are still parsing: that is queued automatically, as described in step 2.)
6. **Read results.** List the project's results, then fetch individual results to see their extracted values and the citations back to the source pages. You can also open the project in the Parsewise web interface at `https://app.parsewise.ai/projects/<project-uuid>/results` to browse results visually, inspect source citations, and export to Excel.

## Iterating after the first run

You rarely redo all six steps. Typical follow-ups:

- **New documents arrive** → upload them, then re-launch. The pipeline picks up the new files.
- **An agent is wrong** → update its definition, then re-launch. Re-launching is required: existing results were computed against the old definition and are stale until you run again.
- **A new column is needed** → create another agent, then re-launch.

Update the agent with the partial-update operation in the schema. The request body is `PatchedV1AgentRequest`; every field is optional, so you only send what you are changing. Follow the update with a launch on the parent project to recompute results.

### What re-launching actually costs

The launch pipeline is incremental, not a full recompute:

- **Parsing** runs only on documents whose status is still `Pending`. Already-parsed documents are not re-parsed.
- **Extraction** is keyed on each agent/document pair. Pairs that already have the full expected set of extraction queries and are resolved are skipped; new documents and agents whose extraction data has been invalidated do the work.
- **Agent updates invalidate their own data.** Editing destructive fields on an agent (`extraction_instructions`, `value_type`, `examples`, `unit`, `resolution_instructions`, `inconsistency_instructions`, `enable_complex_calculations_in_resolution`, `enable_web_search`) clears the agent's extractions on save, so the next launch re-extracts for that agent against every document in the project. Agents you did not touch are unaffected and reuse their existing results.

## Constraints and gotchas

- **Launch is project-scoped.** There is no "only these documents" or "only this agent" option. Prove an agent out on a small test project before pointing it at thousands of production documents.


## How to read results

The results API exposes both a paginated list response and a detail response; consult the schema for the exact operations and full field set. The list response is good for building tables: each row carries an agent name, a `value_type`, an `extraction_status`, a nested `resolution_result` (resolved value and resolution metadata), and the `dimension_instances` that key the row. The detail response includes the same resolved value plus document-level citations (`sources[]`).

When you consume these responses:

- Link to Entity Details in the UI with `/projects/{id}/agents/{agent_id}/{resolution_result_id}` where `{resolution_result_id}` is `resolution_result.id` on the list row (not the row `id`).
- Parse `resolution_result.value` based on the agent's `value_type` (`string` or `number` today — see the value-type bullet above).
- Use `resolution_result.resolution_status` to filter for potential issues, missing values, or cells that are still resolving (see the FAQ for the full vocabulary).
- Use `resolution_result.references` for inline citations (`[document_name, page_number]` tuples, `page_number` is `null` for web sources) and the detail endpoint's `sources[]` for a document-level citation list.
- If you used `POST /extract/` (schema-driven extract), the `results/schema/` endpoint accepts `?enrich=true` to add per-field consistency status and a deep link back into the Parsewise UI — see "Schema-driven extract > Enriched results".


## How to name agents

Agent names show up everywhere — in the Agents page, in results columns, in citations, in Navi answers — so treat them like column headers in a spreadsheet.

- **Use a short noun phrase that describes the value, not the question.** Prefer `Counterparty name` over `Who is the counterparty?`; prefer `Notice period (days)` over `How many days is the notice period?`.
- **Include units or format hints when they disambiguate.** `Start date (ISO 8601)`, `Purchase price (USD)`, `Tenure (months)`.
- **Keep it under ~40 characters** so it renders cleanly in table headers.
- **Avoid project-specific jargon** that a new teammate would not recognise; the agent will often outlive the reason it was created.
- **One concept per agent.** If the name needs "and", split it into two agents so each value has its own column and its own citations.


## How to iterate on an agent

Treat agent design as an empirical loop, not a one-shot prompt.

1. **Start narrow.** Pick one well-understood document and write the minimal extraction task that works for it.
2. **Run the agent and read every citation.** The citation tells you whether the agent found the right *span*, which matters even when the extracted value happens to look correct.
3. **Look for failure modes, not wins.** Scan results where the agent returned empty, picked the wrong section, or produced a formatting variant you did not expect.
4. **Tighten the task description** to resolve the specific failure, then re-run. Avoid generic wording like "be accurate" — describe the concrete distinction the agent missed.
5. **Expand the document set** once the agent is stable on the seed document. New documents will surface new edge cases; repeat from step 3.
6. **Stop when additional iteration stops changing results.** More words in an extraction task rarely help past that point.


## Writing good extraction tasks

The extraction task is the prompt the agent uses to find and normalise a value on each page.

- **Describe the value first, then where to find it.** "The effective date of the agreement, typically in the preamble or on the signature page" beats "Look at the first page".
- **Specify the output format explicitly.** Dates as ISO 8601, currencies as ISO 4217 codes, amounts as numbers without thousands separators, booleans as `true` / `false`.
- **Pick the right `value_type`.** Only two values are supported end-to-end today:
  - `string` — free text. The default. Use this for anything that is not a pure numeric quantity (dates, booleans, codes, names, clauses, descriptions). Specify the expected format in the extraction task (e.g. "ISO 8601 date `YYYY-MM-DD`", "`true` or `false`") so the agent normalises consistently.
  - `number` — numeric values you will aggregate, compare, or do arithmetic over. Pair with `unit` (e.g. `USD`, `%`, `days`) so resolution and display render correctly.

  The schema also lists `bool`, `date`, and `datetime` for backwards compatibility, but they are not wired up end-to-end in the product today — stick to `string` or `number` for new agents.
- **Call out what *not* to extract.** "Ignore illustrative examples in appendices" or "Do not extract dates from the document metadata".
- **Give disambiguation rules for ambiguous documents.** "If multiple effective dates are present, prefer the one in the signature block over the one in the preamble."
- **Prefer citations-first reasoning.** "Return the clause verbatim in the citation, then normalise" pushes the agent to ground its answer.


## Per-agent capability flags

Two optional booleans on the agent payload change how extraction runs. Both default to `false`. Turn them on per agent only when you need them — they cost extra latency and (for web search) external API usage.

### `enable_web_search`

- **What it does.** After extracting from documents, the agent runs targeted web searches to supplement document-derived values with public web data. Sources come back as citations on the result alongside document citations, and the row's `source_type` can flip from `document` to `web` or `document_and_web` depending on which sources contributed.
- **When to turn it on.** The value you are extracting exists partly outside your documents — credit ratings, sanctions list status, public filings, benchmark figures, exchange rates, enrichment of entities mentioned in the document.
- **When to leave it off.** The value exists entirely inside your uploaded documents, or the data is private or internal and would not appear on the public web.
- **Cost / latency.** Adds a `web_search` stage to the pipeline and external API calls. Run time per agent is materially longer; budget for the extra stage when polling.

### `enable_complex_calculations_in_resolution`

- **What it does.** Enables a Python-based resolver stage that can combine per-document extractions using arbitrary arithmetic (weighted averages, conditional sums, growth rates) rather than the default "pick the most relevant value" resolver.
- **When to turn it on.** Your agent needs to aggregate across documents — totals, averages, weighted blends, year-over-year deltas — rather than return a single canonical value per cell.
- **When to leave it off.** You want a single canonical value per cell and the default resolver's precision/frequency heuristics are fine.
- **Cost / latency.** Adds a resolution-stage compute step per result; each cell takes noticeably longer to resolve. Expect `resolution_status` to stay `Not resolved` for longer while the resolver runs.

Both flags are destructive fields: flipping either on or off clears the agent's existing extractions on save, so the next launch re-extracts that agent across every document. See "Iterating after the first run > What re-launching actually costs" for the full list of destructive fields.


## How many values an agent produces

By default an agent produces exactly one value per project, aggregating evidence across every document. That is the right behaviour for "single answer" fields where every document feeds into one canonical value — a single counterparty name, a single effective date, a single consolidated purchase price.

To get more than one value out of an agent, attach a **dimension**. The agent then produces one value per dimension instance, yielding one row per instance in the results. Dimensions come in two flavours:

- **Custom dimensions** you define and attach to specific agents (e.g. "Clause", "Party", "Region") — one value per instance.
- The **system `Document` dimension**, attached automatically to every agent in the project when the project has `per_document_mode` on — one value per uploaded document. Use this when every document is an independent subject rather than evidence for a shared answer (one contract per file, one report per file).

Rules of thumb:

- **Default to no dimension** when the whole project contributes to a single answer.
- **Add a custom dimension** when the value repeats per entity inside the document(s).
- **Enable `per_document_mode` at project creation** when each document is an independent subject and you want one row per file. Navi and schema-driven setup may propose this automatically when the requested output shape clearly implies one row per uploaded document.
- **Keep dimensions stable across agents in the same project** when those agents describe the same underlying entity, so their results join cleanly on the same key.
- **Name the entity in the agent name** (`Lease obligations`, `Line items`) when a custom dimension is attached, so it is obvious at a glance that each row is an entity, not a project-level value.


## When to launch

Launching consumes compute and time, so batch changes intentionally:

- Re-launch after any agent edit and after any document upload — see "Iterating after the first run" for exactly what the next launch will recompute.
- Prove new agents on a small project first (few pages) — launches are project-scoped (see "Constraints and gotchas").


## FAQ

These are the questions the sections above and the OpenAPI schema do not, on their own, fully answer.

### What do `per_document_mode` and `per_tag_mode` on a project actually do?

They are two mutually-exclusive "row shape" modes for the whole project. When `per_document_mode` is on, a system-managed `Document` dimension is attached to every agent in the project with one instance per uploaded document, so result tables get one row per document. `per_tag_mode` does the same thing but keyed by document tags rather than filenames. Both default to off. Leave them off unless you specifically want that row shape — for most analytical work, defining your own dimensions on individual agents is more flexible.

### Can I turn `per_document_mode` on or off on an existing project via the API?

Set the mode when you create the project — not by PATCHing an existing one. Flipping the flag through v1 `PATCH /projects/{id}/` does not run the cleanup that has to accompany the change (wiping stale results, attaching or removing the system `Document` dimension across every agent), so you can end up with stale results and inconsistent agent configuration. If you need to change the mode of a project that already has data, recreate the project with the desired mode and re-upload the documents — both steps are supported in v1.

Alternatively, if you have access to the UI for this organization, you can toggle the mode on the project's Agents page. The UI calls an internal (non-v1) endpoint that performs the required cleanup. This is a human-only workflow; there is no equivalent v1 endpoint to call from a script today.

### The project status endpoint sometimes reports `parsing_state: stuck`. What does that mean?

It means at least one document has had a parsing run in flight for over an hour. A background monitor will have already auto-retried parsing once about 15 minutes in; `stuck` indicates that retry did not clear the problem. There is no v1 endpoint to force another retry. If the state persists, delete and re-upload the affected documents (both supported in v1), or contact support.

### How do I tell, strictly, that a run is complete and succeeded?

Check two signals together. `pipeline_running=false` on `agents/status/` means the backend is no longer actively processing — but it also flips to false when validation fails or a run is cancelled, so it alone does not prove success. For a strict "done and successful" check, also fetch the agents list and confirm every agent reports `extraction_status=Processed`. If some agents are still `Pending` while `pipeline_running` is false, something went wrong during the run — do not treat the results as final.

### What do the `resolution_status` values on a result mean?

The resolver tags every result cell with one of five values:

- **`Resolved`** — all sources agreed (or disagreements were reconciled) and the resolver has produced a single canonical value. Safe to consume.
- **`Requires attention`** — the resolver detected inconsistencies across sources and could not auto-resolve them. The result still carries a chosen value; the status is advisory and often means the agent instructions are too broad or noisy.
- **`Not resolved`** — the resolver stage has not yet run for this cell. Usually means the pipeline is still in progress; poll again.
- **`No result`** — the extraction stage produced no candidates the resolver could work with. Typical causes: the value does not appear in the documents, confidence scores were all below threshold, or dimension instances did not match any extractions.
- **`Ignored`** — a user or a resolution rule explicitly excluded every candidate source, leaving nothing to resolve. Treat as intentional.

Use the non-`Resolved` statuses to highlight cells that may need review or instruction tuning: `Requires attention`, `Not resolved`, `No result`, and `Ignored`. Many `Requires attention` cells still contain useful values; inspect the citations and inconsistency details before deciding whether to exclude them downstream.

### What do the `extraction_status` values on an agent or result mean?

Extraction status reports the state of the extraction stage. It appears both on the agent (rolled up across its rows) and on each result row:

- **`Pending`** — extraction has not yet run, or is in flight.
- **`Processed`** — extraction finished successfully.
- **`No Result`** — extraction finished but produced no candidate values. On an agent this usually points at an extraction task that does not match the documents; iterate on the task definition and re-launch.

### What do the four pipeline stages (`parsing`, `extraction`, `web_search`, `resolution`) actually do?

- **parsing** — text and layout extraction from uploaded documents, page by page. Populates the text and images the rest of the pipeline reads.
- **extraction** — each agent finds candidate values on the relevant pages by running its extraction instructions against them.
- **web_search** — supplements those candidates with public web data, only for agents that have web search enabled.
- **resolution** — consolidates the per-page, per-source candidates into a single final value per result cell, checking for and flagging inconsistencies across sources.


## Schema-driven extract (convenience endpoint)

If you already know the output shape you want, `POST /extract/` collapses the entire create-project → upload → configure → launch flow into a single multipart request. You supply the files and a JSON Schema describing the desired output; Parsewise creates a project, auto-generates agents from the schema, and runs the full pipeline in the background.

### When to use it

- You have a target JSON Schema and want results shaped to it.
- You do not need to hand-tune individual agent instructions before the first run.

Use the step-by-step flow described in "The usual flow" when you need to customise agent instructions, attach dimensions, or iterate agent-by-agent.

### Request

```
curl -X POST -H "X-API-Key: $PARSEWISE_API_KEY" \
  -F 'files=@report.pdf' \
  -F 'files=@accounts.xlsx' \
  -F 'schema={"type":"object","properties":{"revenue":{"type":"number"},"ceo":{"type":"string"}}}' \
  -F 'project_name=Quick extract' \
  "https://api.parsewise.ai/api/v1/extract/"
```

| Field | Type | Required | Description |
|---|---|---|---|
| `files` | file(s) | yes | One or more document files (repeat the field for multiple files). |
| `schema` | JSON string | yes | A valid JSON Schema (Draft 2020-12) describing the desired output. |
| `project_name` | string | no | Name for the auto-created project. Defaults to "API Extraction". |

### Response (`202 Accepted`)

```json
{
  "project_id": "<uuid>",
  "status_url": "/api/v1/projects/<uuid>/status/",
  "results_url": "/api/v1/projects/<uuid>/results/schema/"
}
```

### Poll and read results

```
curl -H "X-API-Key: $PARSEWISE_API_KEY" \
  "https://api.parsewise.ai/api/v1/projects/<project-uuid>/status/"
```

Poll until `pipeline_running` is `false` and `schema_status` is `success`, then fetch the schema-shaped results:

```
curl -H "X-API-Key: $PARSEWISE_API_KEY" \
  "https://api.parsewise.ai/api/v1/projects/<project-uuid>/results/schema/"
```

The response body is a JSON object whose shape matches the schema you submitted, with values populated from the documents.

If the schema implies one output item per uploaded document (for example, an array of per-document objects), schema-driven setup may enable `per_document_mode` on the new project so agents produce one row per file.

### Enriched results

Append `?enrich=true` to get per-field metadata alongside each value. For every scalar leaf in the output, two sibling keys are added:

- `<field>_consistency` — resolution status for the field (same values as `resolution_status` on results: `Resolved`, `Requires attention`, `Not resolved`, `No result`, or `Ignored`).
- `<field>_parsewise_url` — deep link into the Parsewise UI for the underlying resolution result.

```
GET /projects/<project-uuid>/results/schema/?enrich=true
```

### Minimal Python example

```python
import os, time, requests, json

BASE = "https://api.parsewise.ai/api/v1"
H = {"X-API-Key": os.environ["PARSEWISE_API_KEY"]}

schema = {
    "type": "object",
    "properties": {
        "revenue": {"type": "number"},
        "ceo": {"type": "string"},
    },
}

resp = requests.post(
    f"{BASE}/extract/",
    headers=H,
    files=[("files", open("report.pdf", "rb"))],
    data={"schema": json.dumps(schema), "project_name": "Quick extract"},
)
resp.raise_for_status()
project_id = resp.json()["project_id"]

while True:
    status = requests.get(
        f"{BASE}/projects/{project_id}/status/", headers=H
    ).json()
    if not status["pipeline_running"] and status.get("schema_status") == "success":
        break
    time.sleep(5)

results = requests.get(
    f"{BASE}/projects/{project_id}/results/schema/", headers=H
).json()
print(json.dumps(results, indent=2))
```


## Appendix: step-by-step end-to-end example

A minimal happy-path flow from project creation to reading results using the step-by-step endpoints (create project → upload → create agents → launch → poll → read). For a single-call alternative when you already have a target JSON Schema, see "Schema-driven extract" above.

### 1. Create a project

```
curl -X POST -H "X-API-Key: $PARSEWISE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name": "Q4 leases", "description": "Lease extraction"}' \
  "https://api.parsewise.ai/api/v1/projects/"
```

Response (trimmed):

```json
{ "id": "<project-uuid>", "name": "Q4 leases" }
```

### 2. Upload a document

```
curl -X POST -H "X-API-Key: $PARSEWISE_API_KEY" \
  -F 'files=@lease.pdf' \
  "https://api.parsewise.ai/api/v1/projects/<project-uuid>/documents/"
```

### 3. Create an agent

```
curl -X POST -H "X-API-Key: $PARSEWISE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Annual rent (USD)",
    "extraction_instructions": "Extract the annual rent in USD as a number.",
    "value_type": "number",
    "unit": "USD"
  }' \
  "https://api.parsewise.ai/api/v1/projects/<project-uuid>/agents/"
```

### 4. Launch extraction

```
curl -X POST -H "X-API-Key: $PARSEWISE_API_KEY" \
  "https://api.parsewise.ai/api/v1/projects/<project-uuid>/agents/launch/"
```

Returns `202 Accepted` with an empty JSON object.

### 5. Poll status

```
curl -H "X-API-Key: $PARSEWISE_API_KEY" \
  "https://api.parsewise.ai/api/v1/projects/<project-uuid>/agents/status/"
```

Poll as described in step 5 of "The usual flow" — stop when `pipeline_running=false` *and* every agent reports `extraction_status="Processed"`.

### 6. Read results

```
curl -H "X-API-Key: $PARSEWISE_API_KEY" \
  "https://api.parsewise.ai/api/v1/projects/<project-uuid>/results/"
```

Each row follows the shape documented in "How to read results" above.

### 7. View results in the web interface (optional)

Open the project in your browser to browse results visually, inspect source citations with bounding boxes on the original document pages, and export to Excel:

```
https://app.parsewise.ai/projects/<project-uuid>/results
```
