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

# Reports API

> CRUD for report templates, schedules, runs, and artifact downloads.

<Note>
  **See also**: [Reports concept and configuration](/operations-reporting/reports) for what this API powers, when to call it, and how it is configured.
</Note>

All report routes live under `/api/v1/reports/*`, require tenant
authentication, and scope every read/write to the caller's tenant.

## Authentication

Same headers as every other v1 route:

* `X-API-Key: krn_…` plus `X-Tenant-Id: <uuid>`, or
* a session cookie bound to a tenant-linked user account.

RBAC:

* **Viewer, editor, admin**: all read routes + preview.
* **Editor, admin**: create/update/delete templates, schedules; run-now.
* **Admin**: only admin-specific routes (none here).

## Templates

### `GET /api/v1/reports/templates`

List tenant-scoped templates, newest first.

```json theme={null}
{
  "data": [
    {
      "id": "uuid",
      "name": "Weekly digest",
      "description": "…",
      "dataSources": [...],
      "formats": ["pdf", "csv"],
      "narrative": { "enabled": true },
      "enabled": true,
      "createdAt": "…",
      "updatedAt": "…"
    }
  ]
}
```

### `POST /api/v1/reports/templates`

Create a template.

**Body**:

```json theme={null}
{
  "name": "Weekly digest",
  "description": "Offer + channel roll-up",
  "dataSources": [
    { "sourceKey": "offer_performance", "params": { "days": 7 } },
    { "sourceKey": "channel_effectiveness", "params": { "days": 7 } }
  ],
  "sections": [
    { "id": "s1", "title": "Offers",   "dataSourceIndex": 0 },
    { "id": "s2", "title": "Channels", "dataSourceIndex": 1 }
  ],
  "formats": ["pdf", "csv"],
  "narrative": { "enabled": true, "prompt": "Call out retention risks." },
  "filters": { "destinations": ["<provider-uuid>"] },
  "enabled": true
}
```

Returns **201** with the created record.

### `GET /api/v1/reports/templates/:id`

Returns **404** when not found or cross-tenant.

### `PATCH /api/v1/reports/templates/:id`

Partial update. Any of the create-body fields above may be supplied.

### `DELETE /api/v1/reports/templates/:id`

Returns **204**. Cascade-deletes any schedules attached to the template.

### `POST /api/v1/reports/templates/:id/preview`

Run the template once without dispatching or persisting. Returns the
sections, narrative, and base64 artifacts inline.

```json theme={null}
{
  "sections": [
    { "id": "s1", "title": "…", "columns": [...], "rows": [...] }
  ],
  "narrative": {
    "executiveSummary": "…",
    "sectionNarratives": [{ "sectionId": "s1", "narrative": "…" }],
    "keyTakeaways": ["…"]
  },
  "artifacts": [
    { "format": "pdf", "mimeType": "application/pdf", "fileName": "weekly-digest.pdf", "base64": "…", "bytes": 23456 }
  ]
}
```

### `POST /api/v1/reports/templates/:id/run-now`

Execute immediately with `dispatch: true, persistRun: true`. Returns
**202** with the `runId`, artifact metadata, and per-destination
delivery results. Synchronous for Phase 03 — long-running reports block
the HTTP response.

## Schedules

### `GET /api/v1/reports/schedules`

List all schedules for the tenant.

### `POST /api/v1/reports/schedules`

```json theme={null}
{
  "templateId": "<uuid>",
  "name": "Daily 8am",
  "cronExpression": "0 8 * * *",
  "timezone": "UTC",
  "destinations": ["<provider-uuid>"]
}
```

Validates the cron expression via `cron-parser` and computes the initial
`nextRunAt`. Returns **404** when `templateId` is unknown or belongs to
a different tenant. Returns **422** on invalid cron.

### `GET /api/v1/reports/schedules/:id`

### `PATCH /api/v1/reports/schedules/:id`

Recomputes `nextRunAt` when `cronExpression` or `timezone` changes.

### `DELETE /api/v1/reports/schedules/:id`

## Runs

### `GET /api/v1/reports/runs`

Paginated run history.

Query params:

* `templateId`  — filter by template
* `limit`       — 1..200, default 50
* `offset`      — default 0

Response omits `artifactPayloads` (base64) to keep the list JSON small.

```json theme={null}
{
  "data": [
    {
      "id": "…",
      "templateId": "…",
      "scheduleId": "…",
      "status": "completed",
      "triggeredBy": "cron",
      "startedAt": "…",
      "completedAt": "…",
      "durationMs": 4231,
      "deliveryResults": [
        { "providerId": "…", "ok": true, "providerMessageId": "…" }
      ]
    }
  ],
  "pagination": { "total": 42, "limit": 50, "offset": 0 }
}
```

### `GET /api/v1/reports/runs/:id`

Full run record including `sectionsData` and `narrativeOutput`.
Artifact list is returned as metadata only (no base64) — use the
dedicated download endpoint to stream binaries.

### `GET /api/v1/reports/runs/:id/artifacts/:format`

Download the rendered artifact binary for the given format. Returns
the file with the correct `Content-Type` and a
`Content-Disposition: attachment; filename="…"` header. 404 when the
format was not rendered for the run.

Supported `:format` values are: `pdf`, `csv`, `markdown`, `html`.
