Report completed
A requested report finished, successfully or not.
Delivered to the ONE url you registered for reports. Return any 2xx to acknowledge; anything else is retried with exponential backoff. Branch on data.status: ready means the files exist and are fetchable, failed means they never will be for that request.
Request
- idstring
Unique event id — the deduplication key.
Deliveries are at-least-once and unordered, so you WILL receive the same event more than once. Every copy of one event carries the same
id: retries of a delivery, and the deliveries to each url you have registered. Two legitimate transitions of the same subject are two different events and carry two different ids.Key your idempotency on this field alone. Nothing else in the payload is a dedup key —
timestampis the moment the attempt was SENT and differs between copies of one event. - eventstring
Always
report.completed.Possible values: [report.completed] - timestampstring<date-time>
When this delivery was SENT — not when the event happened. Order by the transported state, never by arrival.
- organization_idstring
Your organization id, also carried on the delivery's
X-Organization-IDheader.Snake case on this ONE field is a long-lived wire value; every other field on this API is camelCase.
Treat it as a sanity check, never as proof of origin — it is a value inside a body anyone who learns your url can POST. Proof of origin is the
X-OneTablet-Signatureheader, which store-event deliveries carry on every attempt; see "Verifying webhook signatures". Phase 0 order deliveries are not signed yet, so for those there is no cryptographic authentication at all. - dataReport
The report and its terminal
status— the same objectfetchReportreturns, withfilesomitted for the reason above.
{
"id": "00000000-0000-0000-0000-000000000000",
"event": "report.completed",
"timestamp": "2026-01-01T00:00:00Z",
"organization_id": "00000000-0000-0000-0000-000000000000",
"data": {
"reportId": "00000000-0000-0000-0000-000000000000",
"type": "<type>",
"platform": "<platform>",
"status": "ready",
"periodStart": "<periodStart>",
"periodEnd": "<periodEnd>",
"requestedAt": "2026-01-01T00:00:00Z",
"completedAt": "2026-01-01T00:00:00Z",
"files": [
{
"downloadUrl": "<downloadUrl>",
"expiresAt": "2026-01-01T00:00:00Z",
"sizeBytes": 50,
"format": "csv"
}
],
"error": "<error>"
}
}About Reports
Partner reporting. Coverage and every report are tenant-scoped and never global: the organization is the one the API key authenticates, and no request field can name another.
createReport is a write — a read-only API key is refused with 403.
Idempotency-Key is accepted on createReport and IGNORED today, so a retried createReport requests a second report with a new reportId. That wastes platform budget but corrupts nothing — each report is an independent file. Treat a timeout as "may have succeeded" and check fetchReports before retrying.