Flow · 10 steps
Build, save and run a report
Save a report, run it, and trace a figure back to what was recorded.
Reports read a copy of every service's records, never the live ones. This walk saves a report under an rpt_ id you make (its idempotency key), shows a retry changes nothing, replaces it, runs it, follows a cell back to its facts, and reads how up to date the copy is. A new organization's figures are zero: the walk shows how it works, not data.
Services involved
A filled dot is a read; a ring is a call that changes something.
- Reporting Manager:
https://reporting.getmanifold.io
Every call carries Authorization: Bearer $MANIFOLD_TOKEN unless its step says otherwise
(getting a token). Run a flow in your sandbox, not your live
organization: see Before you start.
1 Discover what can be reported on
The entity graph, filtered server-side to the caller's jobs: the reportable entities with their labelled dimensions and measures, and the declared edges between them. A builder driven by this answer can only offer what a save or a run would accept — a field your jobs may not see is simply not in the model.
GET /model Reporting Manager
What the answer holds: the operation's example.
2 Save a report
The client mints the rpt_ id: it is the document's identity and the idempotency key in one. The definition is validated against the entity graph with the caller's jobs before anything is written — a field your jobs may not see refuses the save exactly as it would refuse the run.
POST /reports Reporting Manager
{
"reportId": "rpt_01a1586d-1d4c-7444-8444-444444444444",
"name": "Units moved by SKU",
"definition": {
"entity": "stockMovement",
"hops": [
"sku"
],
"groupBy": [
{
"field": "sku"
}
],
"measures": [
"quantity"
],
"period": {
"_tag": "preset",
"preset": "30d"
}
}
}Answers 201. What the answer holds: the operation's example.
Records reporting.report-saved.
3 Retry the save
The same reportId again — even with a different name — is acknowledged with the originally saved report and appends nothing. The empty appends here is an assertion the runner holds against the journal: a second fact would fail this step.
POST /reports Reporting Manager
{
"reportId": "rpt_01a1586d-1d4c-7444-8444-444444444444",
"name": "A name the retry does not get to set",
"definition": {
"entity": "stockMovement",
"hops": [
"sku"
],
"groupBy": [
{
"field": "sku"
}
],
"measures": [
"quantity"
],
"period": {
"_tag": "preset",
"preset": "30d"
}
}
}Answers 201. What the answer holds: the operation's example.
Records nothing.
4 Rename it
A replace is idempotent by content: this one changes the name, so it appends the fact with the report as it reads afterwards. Sending the identical body again would append nothing.
PUT /reports/rpt_01a1586d-1d4c-7444-8444-444444444444 Reporting Manager
{
"name": "Units by SKU, last 30 days",
"definition": {
"entity": "stockMovement",
"hops": [
"sku"
],
"groupBy": [
{
"field": "sku"
}
],
"measures": [
"quantity"
],
"period": {
"_tag": "preset",
"preset": "30d"
}
}
}What the answer holds: the operation's example.
Records reporting.report-saved.
5 The library
Lists the saved reports whose every referenced field the caller's jobs may see — a ledger report is simply absent for someone without the account job.
GET /reports Reporting Manager
What the answer holds: the operation's example.
6 Run it
The reference interpreter executes the saved definition over the read model and answers the grouped rows plus asOf — the fold's per-source watermarks at the moment of the read, so a consumer can tell "zero" from "not folded yet". A run appends nothing: it is a pure query, and the runner checks that silence too.
POST /report-runs Reporting Manager
{
"reportId": "rpt_01a1586d-1d4c-7444-8444-444444444444"
}What the answer holds: the operation's example.
Records nothing.
7 Drill a cell
Any leaf cell resolves back to the distinct fact rows that summed into it — each with its stable row id and the journal evt_ ids it was folded from. On this new organization the cell is empty, and the drill says so with zero rows rather than an error.
GET /report-runs/drill?reportId=rpt_01a1586d-1d4c-7444-8444-444444444444&measure=quantity&key=%5B%22WIDGET-1%22%5D Reporting Manager
What the answer holds: the operation's example.
8 How fresh are the numbers
The operational readout behind asOf: per-source replication-slot health and the fold's watermarks with the last run's outcome. Needs the account or admin job — freshness of financial figures is a finance concern.
GET /replica/status Reporting Manager
What the answer holds: the operation's example.
9 The journal shows exactly two saves
The report library is journalled like everything else: the create and the rename are reporting.report-saved facts with the house envelope, and the acknowledged retry between them is nowhere.
GET /events?since=0 Reporting Manager
What the answer holds: the operation's example.
10 Where the journal stands now
The head of the report library's journal at this moment — the last sequence recorded by the database's clock. A replay with upTo set to it reads the library exactly as it stood, whatever is saved after.
GET /events/head?at=2026-11-07T12%3A39%3A42.911Z Reporting Manager
What the answer holds: the operation's example.
What a recorded fact is: Facts and the journal. Why a repeated step changes nothing: Idempotency and retries.