Skip to main content
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 GET 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.
{
  "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:
{
  "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 fields from POST may be supplied.

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

Returns 204. Cascade-deletes dependent ReportSchedule rows.

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

Run the template once without dispatching or persisting. Returns the sections, narrative, and base64 artifacts inline.
{
  "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

{
  "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.
{
  "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 correspond to registered ReportFormats: pdf, csv, markdown, html.