Share links let a report run or decision trace be viewed at a /share/{token} URL without a
login — the token itself is the credential, like a password. Management endpoints are tenant-scoped
and RBAC-gated; the public read endpoint is auth-free and validates the token itself.
POST /api/v1/shares
Issue a new share token. Editor or admin.
Request body
| Field | Type | Required | Description |
|---|
kind | enum | Yes | "report_run" or "decision_trace". |
resourceId | string (1-256) | Yes | Id of the report run or decision trace to share. |
expiresAt | string (ISO-8601) | Yes | When the link expires. Must be in the future and at most 90 days out. |
label | string (max 256) | No | Human label for the link list. Defaults to "". |
kind does not accept "dashboard". Dashboards are config-driven (there is no Dashboard
entity to resolve), so a dashboard token would always 404. The value is intentionally excluded
from the create schema.
Response 201
The token is returned only once, at creation time. Store it — list responses omit it.
{
"id": "shl_...",
"kind": "report_run",
"resourceId": "rr_...",
"token": "9f2c...<64 hex chars>",
"label": "Q3 board deck",
"expiresAt": "2026-08-01T00:00:00.000Z",
"createdAt": "2026-07-03T12:00:00.000Z",
"publicUrl": "/share/9f2c..."
}
Errors
| Code | Reason |
|---|
400 | Invalid payload, expiresAt not a valid timestamp, in the past, or more than 90 days out. |
403 | Caller is not editor/admin. |
GET /api/v1/shares
List this tenant’s share links. Viewer, editor, or admin.
The token is never returned here — only metadata and usage counters.
Response 200
{
"data": [
{
"id": "shl_...",
"kind": "report_run",
"resourceId": "rr_...",
"label": "Q3 board deck",
"expiresAt": "2026-08-01T00:00:00.000Z",
"createdBy": "user_...",
"revoked": false,
"viewCount": 12,
"lastViewedAt": "2026-07-03T18:30:00.000Z",
"createdAt": "2026-07-03T12:00:00.000Z",
"updatedAt": "2026-07-03T18:30:00.000Z"
}
]
}
DELETE /api/v1/shares/:id
Revoke a share link. Editor or admin. Revocation is soft — the row stays with revoked: true
so the audit trail is preserved, and the public read endpoint immediately refuses the token.
Response 200
{ "ok": true, "revoked": true }
GET /api/v1/public/shares/:token
Auth-free read of a previously issued share. No tenant header, no API key — the token in the path
is the credential. Per-IP rate limited (60 requests / 60 s, fail-open).
Response 200
{
"kind": "report_run",
"label": "Q3 board deck",
"expiresAt": "2026-08-01T00:00:00.000Z",
"payload": { "...": "the report-run or decision-trace record" }
}
For report_run the payload excludes raw artifact bytes (metadata, section data, and narrative
only). Each successful read increments the link’s viewCount.
Every failure mode returns 404 — token not found, revoked, expired, or the underlying resource
deleted — so a caller can never enumerate which tokens exist.