Skip to main content

Reports: generate now or schedule delivery

Orbit produces analytics reports two ways:
  • Ad hoc (generate now). Open Insights → Reports in the dashboard, pick a date range, and click a report card. The report renders in the page, downloads as CSV or PDF, and its metadata is saved to your account so it syncs across your devices.
  • Scheduled (recurring email). A scheduled report emails a fixed recipient list on a daily, weekly, or monthly cadence, with the metrics rendered in the body and a PDF attached.
A scheduled report has a name, a report type (the metrics section it contains), a frequency, a recipients list of 1–50 email addresses, and an optional IANA timezone the cadence anchors to. Scheduled-report types: All scheduled-report endpoints live under /api/v1/analytics/scheduled-reports. This is the only write surface for scheduled reports — the older /api/v1/reports/scheduled CRUD was removed; any report it created was migrated automatically, so nothing was lost.

Generate a report now (ad hoc)

For a one-off report, you don’t need the API or a schedule at all:
  1. Open Insights → Reports in the dashboard.
  2. Pick the reporting window with the 7d / 30d / 90d / 12m switcher. Reports honor the selected range, so the figures match the analytics KPIs for the same window.
  3. Click a report card. The report generates immediately and opens in the page viewer.
  4. Download it as CSV or PDF from the viewer toolbar.
The card grid produces five report types, each as a table of columns and rows: Two SLA sections sit below the card grid on the same page:
  • SLA attestation — a monthly attestation of your messaging delivery SLA. Pick a recent complete calendar month, read the summary, download as CSV or PDF.
  • Queue SLA — per-queue service-level figures over a reporting window, with a paginated breach ledger. Pick a window, read the summary, download as CSV or PDF.

The saved-report lifecycle

Every report you generate is saved. The Report history table at the bottom of the page lists the most recent 50 reports per account, newest first, and syncs across devices — reports generated on one machine appear on any other machine where you sign in. A per-browser local cache shows the list instantly while the account copy loads. Only report metadata is persisted — the type, name, generated-at timestamp, and column headers. Row-level data is deliberately never saved: a report’s rows can contain customer phone numbers, email addresses, and contact names, so nothing that sensitive sits in browser storage or syncs to your account. The practical consequences:
  • Reopening a historical report shows its columns with zero rows, and the download buttons are disabled. Click Re-run report on the empty state to load fresh figures, then export.
  • A report generated more than 24 hours ago is flagged Stale in the history table; regenerate it for accurate numbers.
  • Deleting beyond the 50-report retention happens automatically — the oldest entries drop off as new reports are generated.

Downloads and formats

CSV filenames follow <report-type>-YYYY-MM-DD.csv, stamped with the report’s generation date. Timestamps in the viewer and history table are rendered in your workspace timezone. Numeric CSV columns carry numbers or empty cells — never N/A — so spreadsheet SUM and sort keep working.

Schedule or generate once?

  • Generate now when you need a figure once — a board deck, a billing review, an incident report. It’s immediate: no recipients to manage, no schedule to clean up, and the saved history means you can re-run the same report next week in one click.
  • Schedule when the same people need the same numbers every week or month without signing in — the report lands in their inbox at 09:00 local on the cadence tick. Scheduled delivery also reaches stakeholders who don’t have dashboard access, since recipients are plain email addresses.
Both paths produce the same underlying analytics; a scheduled report’s type (messaging volume, deliverability, top contacts, spend) maps onto the same figures the dashboard cards compute on demand.

Create a scheduled report

Create one with POST /api/v1/analytics/scheduled-reports:
  • recipients accepts an array of emails or one comma-separated string; duplicates (case-insensitive) are dropped. Every entry must be a valid email, and the list is capped at 50 addresses. Expect a 400 (VALIDATION_ERROR) when a recipient is malformed.
  • timezone is optional. When set, the cadence anchor is validated as an IANA zone name (America/New_York, Asia/Tehran, …). When omitted, the cadence anchors to UTC.
  • enabled defaults to true; create with "enabled": false to stage a report without sending it.
  • filters is an optional JSON object. For custom reports it carries the saved-query definition, and filters.dataset must be messages or queue_performance when present.

How the cadence tick is computed

Every report fires at 09:00 local time — never at the moment you created it, and never a raw interval like NOW() + 30 days:
  • daily → next day at 09:00
  • weekly → the start of next week at 09:00
  • monthly → the first day of the next month at 09:00
Anchoring to calendar boundaries matters: an interval-based monthly schedule drifts by several days per year, and a monthly report created on the 31st would slowly migrate earlier in the month. Anchoring keeps a monthly report on the 1st and a weekly report on the same weekday, indefinitely. When the stored next_send_at instant has already arrived — for example right after a send-now request, or in the short window between a tick becoming due and the scheduler claiming it — the API does not echo that past/now value back. Because send-now works by marking the report due, the raw value would otherwise read as “now” (or a past instant) even though the recurring schedule didn’t change. Instead the API returns the upcoming cadence tick, so the value you read (and the dashboard renders) is always the next real 09:00 tick, never a transient due-time.

Edit cadence, recipients, or sections

PATCH /api/v1/analytics/scheduled-reports/{id} accepts any subset of fields; omitted fields keep their current values. The endpoint requires at least one field.
Two update behaviors are worth knowing:
  • Frequency changes re-anchor the schedule. When you change frequency, the next tick is recomputed from the last send (or from now if the report never sent), floored so the result can never be in the past. So switching a stale weekly report to daily makes the next tick tomorrow at 09:00 — not next week minus the elapsed days. It also never triggers an immediate send as a side effect.
  • Recipient edits replace the list. recipients is replaced wholesale, not merged — pass the full final list on every update. The same 1–50 unique-valid-email rules apply.

Send-now: what it does and doesn’t do

send-now marks the report due; the scheduler worker picks it up on its next tick and delivers it. It does not build and send the email inside your HTTP request — the response returns immediately with { "queued": true }, keeping send/retry logic out of the API tier. Poll GET /api/v1/analytics/scheduled-reports and watch last_sent_at to see when it actually lands. It also doesn’t move the cadence. Because the list endpoint presents the upcoming 09:00 tick whenever a report is due, a daily report keeps showing “tomorrow 09:00” after send-now rather than collapsing to “a few minutes from now.”

Delete

Deleting is permanent and returns { "id": "sr_01H…", "deleted": true }. Deleting stops all future sends; previously sent emails are of course unaffected. To pause a report without losing its recipient list and schedule, set "enabled": false on a PATCH instead of deleting it.

Role gate

Reads (GET) are open to any authenticated member of the organization. Writes — create, update, delete, and send-now — require the owner or admin role. A scheduled report emails recipient-level metrics (top contacts, spend, error breakdowns) to an arbitrary list of addresses the creator chooses: that is a data-egress path, so it’s restricted to roles trusted to export org data. If viewer-role tokens could create reports, a read-only member could exfiltrate the same analytics to any mailbox. The role check comes back as a 403 on write endpoints for developer/viewer keys and members.

Troubleshooting

See also