Search

↑↓ to move↵ to openesc to closetop 6 documentation results

Book a demoTry TokenTriage

In development · Explore the product preview. Join the launch list or book a walkthrough.

Browse docs

Reporting and alerts

The report, schedule and inbox operations, the documents they answer with, their states, problem codes, limits and retention, and what alerts do not offer yet.

ReferenceLast updated

On this page

This page lists the contract behind reports: eight operations, the three documents they answer with, the states a run moves through, the problem codes, and the limits and retention that apply. It then lists the same for schedules, which run a report again on a cadence, and for the in-app inbox they deliver to. Alerts are listed at the end with their current status, which is that they are not available yet.

Field names and codes below come from the R01 and R02 wire contracts and the tokentriage cloud report commands. What a report run keeps, and who can open it explains why a run is frozen and how access is checked; this page does not repeat that.

Reports

A report is a named definition that points at one saved view at one revision. Each run of it copies that view’s question, resolves the window once, and keeps one bounded answer.

Operations

All eight share the customer base path /portal/api/v1/customer/v1. Collection rows sit under the workspace; item rows sit under the organization, because a report carries its own workspace. This mirrors the saved-view operations.

Operation Method and path Success Python / TypeScript CLI
customer.reports.list GET /organizations/{org}/workspaces/{ws}/reports 200, list of ReportV1 reports.list cloud report list
customer.reports.create POST /organizations/{org}/workspaces/{ws}/reports 202, {report, run} reports.create cloud report create
customer.reports.get GET /organizations/{org}/reports/{rp} 200, ReportV1 reports.get cloud report get
customer.reports.delete DELETE /organizations/{org}/reports/{rp} 200, {report_id, deleted_at, runs_removed} reports.delete cloud report delete
customer.reports.runs.create POST /organizations/{org}/reports/{rp}/runs 202, ReportRunV1 reports.runs.create cloud report run
customer.reports.runs.get GET /organizations/{org}/reports/{rp}/runs/{run} 200, ReportRunOpenV1 reports.runs.get cloud report show
customer.reports.runs.cancel POST /organizations/{org}/reports/{rp}/runs/{run}/cancel 200, ReportRunV1 reports.runs.cancel cloud report cancel
customer.reports.runs.export GET /organizations/{org}/reports/{rp}/runs/{run}/export?format=json|csv 200, attachment bytes reports.runs.export cloud report export

What each operation does, in one line:

  • list answers the reports in the workspace that you own or are a recipient of, newest first, with the lane’s opaque pagination (--limit up to 100, --page-token).
  • create stores the definition and queues its first run. The run is queued when the 202 arrives; poll it with runs.get.
  • get answers one definition. It carries latest_run_id, not an answer.
  • delete removes the definition and every run of it. Owner only.
  • runs.create is “Run again”: a new run with a new window, a new source fence and a new digest. Earlier runs are unchanged.
  • runs.get polls a run and, once it is ready or partial, opens it: the answer arrives in the same document, redacted for the viewer.
  • runs.cancel moves a queued or running run to cancelled.
  • runs.export downloads one run as an attachment. See Export formats.

The Python client exposes these as client.reports and client.reports.runs; the TypeScript client uses the same names. Both keep the envelope, request IDs, problem documents and nano-USD strings unchanged.

Request bodies

create takes the saved view, the revision of it you read, a name, and optional recipients:

{
  "name": "A1 morning check",
  "saved_view_id": "sview_…",
  "expected_view_revision": "3",
  "recipient_subject_ids": [],
  "evidence_refs": [],
  "idempotency_key": "acme-it-daily-report-01"
}
Field Type Rules
name string 1 to 120 characters, no control characters; unique per owner in the workspace
saved_view_id string a saved view you can currently see
expected_view_revision decimal string the view’s revision as you read it; a mismatch is 412
recipient_subject_ids list of subject IDs current members of the organization, at most 20; may be empty
evidence_refs list must be empty in this release; anything else is 422
idempotency_key string optional in the body; the Idempotency-Key header is the fallback

runs.create takes only the key:

{ "idempotency_key": "acme-it-daily-report-02" }

get, delete, runs.get, runs.cancel and runs.export take no body.

Idempotency

create and runs.create are the two idempotent writes. The key travels in the body as idempotency_key; the Idempotency-Key header is read when the body has none.

You send You get
a new key a new report and run (or a new run), 202
the same key with the same body the report and run the first request created, 202
the same key with a different body 409 conflict

A key is scoped to the workspace and the caller. The CLI mints a fresh key per invocation unless you pass --idempotency-key, and the SDKs take idempotency_key= (Python) or { idempotencyKey } (TypeScript). A create that timed out is retried with the key it was sent with, which answers the first report instead of making a second.

ReportV1

The definition. Revisions are unsigned integers carried as JSON strings, as saved views carry theirs; copy them, do not parse them.

Field Type What it holds
report_id string rpt_…
organization_id string org_…
workspace_id string t_…
owner subject ID the person who created the report
name string as created
saved_view_id string the view the report copies its question from
view_revision decimal string the view revision the report pins
recipient_subject_ids list people who may also see the report
evidence_refs list empty in this release
revision decimal string the definition’s own revision
latest_run_id string rrun_…, the most recent run
created_at, updated_at RFC 3339 UTC

ReportRunV1

One run. create answers it inside {report, run}; runs.create and runs.cancel answer it alone.

Field Type What it holds
run_id string rrun_…
report_id, organization_id, workspace_id string the run’s owners
initiator subject ID who created this run
idempotency_key string the key the run was created with
state enum see Run states
failure_code, failure_detail string empty unless the run failed
question object the insight-query/v1 document copied from the view, scope included
view_revision decimal string the view revision the question was copied at
window object from and to, a half-open UTC window [from, to)
display_tz IANA zone the view’s display zone; it labels times and does not change the window
authority_epoch integer the access generation the run was executed under
attempt integer how many times a worker has tried the run
coverage list coverage reasons the reader reported; empty when complete
truncated boolean true when the answer hit a row bound
artifact_digest string sha256:… over the question, window, view revision and stored answer
group_count integer groups in the stored answer
byte_count integer size of the stored answer
created_at, started_at, finished_at RFC 3339 UTC
expires_at RFC 3339 UTC 30 days after finished_at. For a ready or partial run, this is when the answer is removed and the run becomes expired. A failed or cancelled run carries the same date but has no answer to remove. The run’s metadata stays readable for 90 days either way

ReportRunOpenV1

What runs.get answers: every ReportRunV1 field, plus these.

Field Type What it holds
answer object the insight answer, redacted for the viewer; present only when ready or partial
presentation_digest string sha256:… over the document this viewer received
money_withheld boolean true when money cells were replaced because the viewer lacks cost.read

presentation_digest equals artifact_digest only when nothing was withheld. A redacted presentation carries its own digest and makes no claim to match the stored answer byte for byte.

Answers arrive inside the lane’s envelope. The envelope kinds are reports (list), report-creation (create), report (get and the delete acknowledgement), report-run (runs.create and runs.cancel) and opened-report-run (runs.get). Export has no envelope.

Run states

A run starts queued and settles in one of four states. expired follows ready or partial when retention removes the answer.

State Meaning Answer present
queued waiting for a worker no
running a worker holds the run and is querying no
ready finished with a complete answer; an empty window is ready with “no requests” cells yes
partial finished, and the answer is truncated, carries coverage reasons, or has a cell that is partial or unavailable for a reason other than scope_not_authorized yes
failed finished without an answer; failure_code says why no
cancelled stopped by the owner or initiator before it finished no
expired the stored answer was removed; the run’s metadata is still readable no

failure_code is one of these, or a reader refusal code recorded as the reader sent it (for example read_context_expired or metric_version_unknown):

failure_code What happened
scope_unavailable the initiator’s access no longer covered the frozen scope when the run executed; the window and question were kept
output_cap_exceeded the answer passed 1,000 groups or 10 MiB
attempts_exhausted five transient failures (reader or store unavailable, or a lapsed worker lease)

Transient failures are retried with exponential backoff, 2^n seconds capped at 5 minutes, up to five attempts. Lost access, an unknown metric version, a validation failure and an expired read context end the run on the first attempt.

Export formats

runs.export answers 200 with Content-Disposition: attachment and no envelope.

format Bytes
json (default in the CLI) the ReportRunOpenV1 document for the viewer
csv the answer flattened to one row per group and cell

Both are redacted for the person exporting, under the same rules as runs.get. The CLI writes the bytes verbatim to --output or stdout and prints the content type and a sha256 of what arrived on stderr. That digest hashes the downloaded file; it is not the run’s artifact_digest.

Problems

Refusals use the lane’s existing problem document and its existing slugs. No slug is new to reports.

Status type When
404 not_found the report or run does not exist, or you are not its owner or a recipient, or your current metadata.read covers less than its frozen scope
403 forbidden a recipient tried to delete, or somebody other than the owner or initiator tried to cancel
409 conflict an idempotency key reused with a different body, a report name you already use in the workspace, or a cancel on a run that has already settled
412 precondition_failed expected_view_revision does not match the view, or on runs.create, the view has moved past the revision the report pins
422 validation_failed a malformed body, a name out of bounds, a recipient who is not a member, more than 20 recipients, or a non-empty evidence_refs
503 dependency_unavailable this deployment has no report store, reader or worker composed, so a report would not run

Insight refusals from the reader, such as coverage_unknown, read_context_expired, metric_version_unknown and scope_required, reach you unchanged. Once a run is queued, a reader refusal ends the run and is recorded as its failure_code.

A 404 and a 403 differ for a reason. Somebody who can see a report but may not remove it is told so; somebody who cannot see it is told there is no such report.

Authority, money and metadata

Reports reuse the saved-view permissions. There is no separate report permission in this release.

Action Who may
create, run again a caller with metadata.read whose access covers the view’s whole scope and who can see the view
list, get, open, export the owner and listed recipients, each checked against their own current access at the moment they read
cancel the report’s owner or the run’s initiator
delete the owner

A narrower access block is concealed as 404; a report is not silently narrowed to what the caller can see. Money cells (nano-USD, the catalog’s money metrics) need cost.read on the viewer’s block. Without it they read unavailable{scope_not_authorized} and money_withheld is true.

Money figures come from the catalog accounting behind the gateway insight reads, with the same unavailable reasons. They are not an invoice. Producing a report makes no provider call and records no inference usage. No document holds prompts, responses or provider secrets.

Runtime keys do not open reports. Use a person’s session or a management key.

Limits and retention

Limit Value Enforcement
report definitions per workspace 50 documented capacity bound; not enforced by a refusal in this release
recipients per report 20 422 validation_failed
groups per answer 1,000 the run fails with output_cap_exceeded
stored answer size 10 MiB the run fails with output_cap_exceeded
attempts per run 5 the run fails with attempts_exhausted
list page size 100 the largest --limit the CLI accepts
stored answer kept 30 days after finished_at the run becomes expired
run metadata kept 90 days

Schedules and inbox

A schedule runs one report again on a cadence, as the report’s owner, and delivers each settled run to the in-app inbox of the schedule’s recipients. Delivery is in-app only: a schedule sends no email and no Slack message. Each occurrence makes an ordinary report run, so everything under Reports applies to it.

See Deliver a gateway report on a schedule to schedule a report you own, read when it will run next, and open a delivered run as a recipient.

Schedule and inbox operations

All eight share the customer base path /portal/api/v1/customer/v1 and the arrangement reports use: collection rows under the workspace, item rows under the organization.

Operation Method and path Success Python / TypeScript CLI
customer.report_schedules.list GET /organizations/{org}/workspaces/{ws}/report-schedules 200, list of ReportScheduleV1 report_schedules.list / reportSchedules.list cloud report schedule list
customer.report_schedules.create POST /organizations/{org}/workspaces/{ws}/report-schedules 201, ReportScheduleV1 report_schedules.create / reportSchedules.create cloud report schedule create
customer.report_schedules.get GET /organizations/{org}/report-schedules/{sch} 200, ReportScheduleV1 report_schedules.get / reportSchedules.get cloud report schedule get
customer.report_schedules.update PUT /organizations/{org}/report-schedules/{sch} 200, ReportScheduleV1 report_schedules.update / reportSchedules.update cloud report schedule update
customer.report_schedules.delete DELETE /organizations/{org}/report-schedules/{sch} 200, {schedule_id, deleted_at, occurrences_cancelled} report_schedules.delete / reportSchedules.delete cloud report schedule delete
customer.report_schedules.occurrences GET /organizations/{org}/report-schedules/{sch}/occurrences 200, page of ReportOccurrenceV1 report_schedules.occurrences / reportSchedules.occurrences cloud report schedule occurrences
customer.report_inbox.list GET /organizations/{org}/workspaces/{ws}/report-inbox 200, page of ReportInboxItemV1 report_inbox.list / reportInbox.list cloud report inbox list
customer.report_inbox.open POST /organizations/{org}/report-inbox/{delivery}/open 200, OpenedInboxItemV1 report_inbox.open / reportInbox.open cloud report inbox open

What each operation does, in one line:

  • list answers the workspace’s schedules you own or receive, newest first.
  • create schedules a report you own, at the report revision you read. The answer states execution_state and the next three occurrences.
  • get answers one schedule you own or receive, with its next three occurrences computed from the current time. There is no separate preview operation.
  • update replaces the cadence, recipients and paused flag under expected_revision. Owner only.
  • delete retires the schedule and cancels its in-flight work. Owner only. Its occurrences and delivered inbox items stay as history, and the schedule leaves every read.
  • occurrences is the execution history, newest scheduled first: what ran, failed, was cancelled or was missed. It is not a forecast.
  • inbox list answers your own delivered items in the workspace, newest first. An empty page means nothing has been delivered to you yet.
  • inbox open checks your current access to the item’s run, then answers the item and the run and records the first read.

occurrences and inbox list page with an opaque cursor: pass cursor (the CLI’s --cursor) from the previous page’s next_cursor, and set limit from 1 to 100; the server’s default is 50. An empty next_cursor ends the list. A forged cursor is 422.

Schedule request bodies

create takes the report, the report revision you read, a cadence, recipients and an idempotency key:

{
  "report_id": "rpt_…",
  "expected_report_revision": "1",
  "cadence": {"mode": "wall_clock", "frequency": "daily", "local_time": "09:00",
              "timezone": "America/Los_Angeles"},
  "recipient_subject_ids": ["<subject>"],
  "paused": false,
  "idempotency_key": "acme-it-schedule-01"
}
Field Type Rules
report_id string a report you own, in this workspace
expected_report_revision decimal string the report’s revision as you read it; a mismatch is 412
cadence object see Cadence
recipient_subject_ids list of subject IDs at most 20; each a current member, the report’s owner or one of its recipients, and able to open the report’s latest run under their own access; may be empty
paused boolean true saves the schedule paused (pause_reason manual)
idempotency_key string optional in the body; the Idempotency-Key header is the fallback

Create is the only idempotent schedule write, with the same rules as a report create: the same key and body answer the first schedule, and the same key with a different body is 409 conflict. The Python client takes idempotency_key=, the TypeScript client { idempotencyKey }, and the CLI --idempotency-key.

update replaces the whole editable document. Every member is required, and an empty recipient_subject_ids removes every recipient:

{
  "expected_revision": "3",
  "cadence": {"mode": "wall_clock", "frequency": "daily", "local_time": "09:00",
              "timezone": "America/Los_Angeles"},
  "recipient_subject_ids": ["<subject>"],
  "paused": true
}

expected_revision is the schedule’s revision as you read it; a stale one is 412, and nothing is merged. The Python client takes it as the expected_revision= argument, the TypeScript client in the body, and the CLI as --expected-revision with the other three members in --file.

get, delete, occurrences, inbox list and inbox open take no body.

Cadence

A cadence has one of two modes. It keeps its mode until an update names another one, and there is no cron expression.

Field interval wall_clock
mode interval wall_clock
frequency hourly, daily or weekly: every 3,600, 86,400 or 604,800 seconds of elapsed time daily or weekly; hourly is not accepted
anchor optional RFC 3339 instant the grid counts from; defaults to the creation instant and is kept by an update that stays interval not accepted
local_time not accepted required, HH:MM from 00:00 to 23:59
timezone optional IANA zone; labels the local side of each occurrence and moves nothing required IANA zone; drives the local time
weekday not accepted required for weekly, monday to sunday; not accepted for daily

A wall_clock daily cadence runs once per local calendar day, and weekly once per local week. When the local time falls in a daylight-saving gap, the occurrence runs later by the length of the gap (02:30 becomes 03:30 for a one-hour gap). When it falls in a fold, it runs once, at the earlier instant. An interval cadence runs on elapsed time, so its local hour shifts by the clock change. Local is not accepted as a timezone.

A cadence that breaks one of these rules is 422 validation_failed, and the problem detail names the cadence code and the field:

Code Meaning
invalid_mode mode is not interval or wall_clock
invalid_frequency frequency is not hourly, daily or weekly
hourly_requires_interval hourly with wall_clock
invalid_timezone an unknown or missing IANA zone, or Local
invalid_local_time local_time is not HH:MM in range
invalid_weekday a weekly wall_clock cadence without a valid weekday
field_not_allowed a field the mode does not take, such as anchor with wall_clock
fixed_view_not_schedulable the report’s view has a fixed window; a repeated historical window is ambiguous, so create a relative view or run the report by hand
invalid_window the report’s relative view has no positive window

ReportScheduleV1

The schedule. Revisions are decimal strings, as elsewhere on this page.

Field Type What it holds
schedule_id string rsch_…
organization_id, workspace_id string where the schedule lives
report_id string the report it runs
owner subject ID the report’s owner, who created the schedule and as whom it runs
cadence object as saved, with an interval anchor filled in
tzdb_version string the time zone database the server resolved the zone with
recipient_subject_ids list who receives each run
paused, pause_reason boolean, string pause_reason is empty unless paused; see Execution and pause states
execution_state enum armed, paused or unavailable
unavailable_reasons list the missing parts when unavailable: dispatcher, report_worker, reader, inbox
revision decimal string bumped by every change you can see
next_run_at RFC 3339 UTC or null the next scheduled instant; null while paused
next_occurrences list the next three {scheduled_utc, local, note}; note is empty, dst_gap or dst_fold
last_scheduled_for RFC 3339 UTC or null the last instant an occurrence was made for
skipped_total integer occurrences passed over by the catch-up rule, over the schedule’s life
created_at, updated_at RFC 3339 UTC

Execution and pause states

execution_state Meaning
armed not paused, and the dispatcher, report worker, reader and inbox are all running
paused paused; pause_reason says why
unavailable not paused, and at least one of those parts is missing from this deployment

A paused schedule reads paused whatever else is missing. A schedule is armed only while every part it needs is present.

pause_reason Set by
manual the owner, on create or update
authority_lost the server, when the owner’s current access no longer covers the report’s view or its whole scope
report_deleted the server, when the report was deleted
view_changed the server, when the view moved past the revision the report pins

Resuming (paused: false) or changing the cadence starts at the next instant after the update. Nothing owed during the pause is run.

ReportOccurrenceV1

One scheduled instant and what happened to it.

Field Type What it holds
occurrence_id string rocc_…
schedule_id, report_id string
schedule_revision, view_revision decimal string the revisions pinned when the occurrence was claimed
scheduled_for RFC 3339 UTC the instant the occurrence is for
window object from and to, half-open UTC: to is scheduled_for, from is to minus the view’s relative window; absent on missed
state enum see the next table
run_id string the rrun_… it made; empty on missed
failure_code, failure_detail string set on failed and cancelled
skipped_count, skipped_from, skipped_to integer, RFC 3339 UTC older instants the catch-up rule passed over, counted on this occurrence
delivered_count, suppressed_count integer recipients who received an item, and recipients who could no longer open the run
attempt integer transient failures retried so far, at most 5
created_at, finished_at RFC 3339 UTC

Recipients are counted on an occurrence, not listed. The claim pins the schedule revision, the recipients, the view revision and the window, so a worker delay, a retry or a later edit moves none of them. The window is elapsed time: 24 hours stays 24 hours across a clock change.

state Meaning
claimed the dispatcher has taken the instant and pinned the window
running its report run exists and has not settled
delivered the run settled and each recipient who can still open it has an item
failed it ended without delivery; failure_code says why
cancelling a pause or delete is cancelling its run
cancelled a pause, delete or run cancel stopped it
missed the latest owed instant was more than 24 hours old; no window and no run
failure_code What happened
authority_lost the owner’s access no longer covers the report’s scope; the schedule pauses authority_lost
report_deleted the report was deleted
view_changed the view moved past the revision the report pins
invalid_request the run could not be created from the pinned inputs
window_mismatch the run’s window differed from the pinned window
run_expired the run’s answer expired before delivery
attempts_exhausted five transient failures
schedule_paused, schedule_deleted, run_cancelled the reason on a cancelled occurrence

A report run’s own failure_code, such as scope_unavailable or output_cap_exceeded, is copied onto the occurrence unchanged.

When the dispatcher runs late, it runs at most one catch-up: the latest owed instant within 24 hours, with the older instants counted in skipped_count, skipped_from and skipped_to. If the latest owed instant is older than 24 hours, a missed occurrence records the count and range, and the schedule moves to its next future instant.

Only one scheduled report runs at a time in a workspace. The dispatcher claims due schedules oldest first, at most one per workspace, and none from a workspace that already has an occurrence in flight. A schedule that waits keeps the window of the instant it was scheduled for.

ReportInboxItemV1

One delivered item. It carries identifiers only: no answer, no money and no prompt.

Field Type What it holds
delivery_id string rdlv_…
organization_id, workspace_id string
event_type enum report_delivered for a scheduled report
source_kind, source_id string report_occurrence and the rocc_… that delivered it
report_id, run_id, schedule_id string the report, run and schedule behind the item
delivered_at RFC 3339 UTC when the item was committed
read_at RFC 3339 UTC or null the first open; later opens do not change it

OpenedInboxItemV1 is {item, run}: the item with read_at set, and run, the ReportRunOpenV1 document redacted for you, as runs.get would answer it.

Delivered means committed and listable in the product. Read means you opened it, not that an email arrived. Each recipient’s current access is checked at delivery: a recipient who can no longer open the run gets no item and is counted in suppressed_count, and the other recipients are unaffected. Opening an item checks your access again, and an open that fails that check records nothing.

Answers arrive inside the lane’s envelope. The envelope kinds are report-schedules (list), report-schedule (create, get, update and the delete acknowledgement), report-schedule-occurrences (occurrences), report-inbox (inbox list) and opened-report-inbox-item (inbox open).

Schedule problems

Problems use the lane’s existing slugs. No slug is new to schedules or the inbox.

Status type When
404 not_found the schedule or item does not exist, was deleted, or is not yours to see; or the report is not visible to you; or you can no longer open the item’s run
403 forbidden a recipient tried to create, update or delete; only the report’s owner may
409 conflict an idempotency key reused with a different body
412 precondition_failed expected_report_revision or expected_revision is stale, or the report pins a view revision the view has moved past
422 validation_failed a cadence code from Cadence, a recipient who is not eligible, more than 20 recipients, a forged cursor, a limit outside 1 to 100, or resuming a schedule whose report was deleted
503 dependency_unavailable this deployment composes no schedule store, dispatcher and report worker, or no inbox; or an item’s source cannot be opened here

The 503 means the schedule lane is missing, and nothing was saved. A schedule that was saved and reads unavailable is different: the lane is present and one part that runs it is not.

Schedule authority

Schedules use the report permissions: metadata.read for the organization, plus the owner and recipient rules below. There is no separate schedule permission.

Action Who may
create, update, delete the report’s owner, who must still see the report’s view at the pinned revision
list, get, occurrences the schedule’s owner and its recipients
inbox list, inbox open the item’s recipient, with their own credential

At each occurrence the owner’s current access is resolved before anything runs, and each recipient’s access is resolved before their item is written. Naming a recipient grants nothing.

Schedule limits

Limit Value Enforcement
schedules per workspace 20 documented capacity bound; not enforced in this release
recipients per schedule 20 422 validation_failed
scheduled reports running per workspace 1 later due schedules wait their turn
next occurrences shown 3
catch-up after a late start 1 occurrence, within 24 hours older instants are counted as skipped or missed
attempts per occurrence 5 the occurrence fails with attempts_exhausted
page size for occurrences and inbox 100, default 50 a larger limit is 422

The run each occurrence makes follows the report limits and retention above: 1,000 groups, 10 MiB, and the answer kept 30 days after the run finishes.

Alerts

Not available yet. There are no alert policies, alert episodes or silences in this release.