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.
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 (
--limitup to 100,--page-token). - create stores the definition and queues its first run. The run is
queuedwhen 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
readyorpartial, opens it: the answer arrives in the same document, redacted for the viewer. - runs.cancel moves a
queuedorrunningrun tocancelled. - 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_stateand 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
pausedflag underexpected_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.