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

# Reports

> Durable, asynchronously generated analysis over documents and data you name.

A report is a durable, asynchronously generated, retrievable artifact produced by
the platform's runtime over inputs you named. It is where you send work that is
too long to hold an HTTP connection open for, and whose result you want to keep.

```bash theme={null}
curl "$IMPEL_API/reports" \
  -H "Authorization: Bearer $IMPEL_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: c40f…" \
  -d '{
    "name": "Q3 supplier risk",
    "agent": "agt_analyst",
    "task": "Compare these contracts and list every clause that differs from our standard terms.",
    "inputs": {
      "documents": ["doc_a", "doc_b", "doc_c"],
      "data": { "standard_terms_version": "2026-04" }
    }
  }'
```

Needs `reports:create`. Answers **202** with a `job_id`.

## The resource is deliberately generic

There is no `report_type` naming anyone's product, and no column only one design
partner would populate. The domain lives in three places that are all yours:

* **which agent runs it** — your assistant, your persona, your knowledge
* **what you asked it for** — `task`
* **what you pointed it at** — `inputs.documents`, `inputs.data`

A talent report and a compliance report are two rows with different values in
those fields and no schema difference at all.

## Text or JSON

`format` decides which output column is authoritative.

<AccordionGroup>
  <Accordion title="format: text — the default">
    Prose. `output.text` carries it.
  </Accordion>

  <Accordion title="format: json — with a schema">
    ```json theme={null}
    {
      "agent": "agt_analyst",
      "task": "…",
      "format": "json",
      "schema": {
        "type": "object",
        "properties": {
          "differences": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "clause":  { "type": "string" },
                "risk":    { "type": "string", "enum": ["low", "medium", "high"] }
              },
              "required": ["clause", "risk"]
            }
          }
        },
        "required": ["differences"]
      }
    }
    ```

    Validated the same way as [structured outputs](/v2/structured-outputs), with
    the same closed schema subset. `output.data` carries the result.
  </Accordion>
</AccordionGroup>

Output is two fields rather than one, because a single field that sometimes holds
prose and sometimes holds JSON is a field whose type a reader has to guess from a
sibling.

## Reading one

```bash theme={null}
curl "$IMPEL_API/reports/rep_…" -H "Authorization: Bearer $IMPEL_KEY"
```

```json theme={null}
{
  "object": "report",
  "id": "rep_…",
  "status": "completed",
  "name": "Q3 supplier risk",
  "agent_id": "agt_analyst",
  "task": "Compare these contracts…",
  "inputs": { "documents": ["doc_a", "doc_b"], "data": {} },
  "output": {
    "format": "text",
    "available": true,
    "characters": 4820,
    "text": "…",
    "data": null
  },
  "generation": {
    "job_id": "job_…",
    "request_id": "req_…",
    "trace_id": "trc_…",
    "failure": null
  },
  "usage": { "…": "…" },
  "metadata": {},
  "created_at": "…", "started_at": "…", "completed_at": "…"
}
```

The `output` key is present whether you are reading a listing or a detail. What
changes is whether the body is in it or only its size — so a client parses one
shape.

On the detail endpoint **both `text` and `data` are always present**, so you do
not have to branch on `format` to know which to read, and so a text report is
visibly *not* carrying an empty JSON object.

## Four statuses, and the job has five

The report says `processing`, `completed`, `failed` or `cancelled`. That is the
**resource**.

"Queued behind three others" and "on attempt two of three" are real questions
with real answers, and they are one dereference away under
`generation.job_id`:

```bash theme={null}
curl "$IMPEL_API/jobs/job_…" -H "Authorization: Bearer $IMPEL_KEY"
```

Needs `jobs:read`. See [Jobs](/v2/jobs). The resource and the job are two
different things, and duplicating the job's vocabulary into the resource would
mean keeping two in step.

## Listing

```bash theme={null}
curl "$IMPEL_API/reports?limit=50" -H "Authorization: Bearer $IMPEL_KEY"
```

Needs `reports:read`. Returns `next_cursor`, read back as **`cursor`**. Listings
carry `output` without its body.

## There is no delete

Three endpoints exist and none of them removes anything. There is no
`reports:delete` scope to be issued.

Retention for generated analysis is a real decision that has not been made — a
report is model output over customer content, so it may end up following the
conversation retention rules. A soft-delete column nothing writes to would be a
promise this API has not made, so there is not one.

## Isolation

A report belongs to a `(workspace, application, environment)` triple, the same as
a [document](/v2/documents). A development credential does not reach a production
report even when both name the same workspace.

## Idempotency

`POST /reports` sets **`Idempotent-Replay: true`** on a replay — not
`Idempotency-Replayed`, which is what the runtime endpoints use.
