Reports API
Every Reports action (5) and data model (2): endpoints, scopes, input, preconditions, events and examples.
Sales and money reports, and who is allowed to see each one.
Module code reports · 5 actions · 2 data models · 4 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: reports (everything), reports:read (read only) or reports:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
report_exports | report_export | /api/v1/report_exports | |||
report_schedules | report_schedule | /api/v1/report_schedules |
Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.
Actions
| Action | Needs | What it does |
|---|---|---|
reports.create_schedule | reports:manage | Schedule a report by email |
reports.delete_schedule | reports:manage | Stop a scheduled report |
reports.record_export | reports:export | Record a report export |
reports.request_export | reports:export | Export every row |
reports.send_digest | reports:manage | Send a scheduled report |
reports.create_schedule
Schedule a report by email — Sends a report’s headline numbers through a notification rule on a fixed calendar.
Needs reports:manage · changes data
https://app.getera.io/api/v1/report_schedules/actions/create_schedule- Canonical address:
POST /api/v1/actions/reports.create_schedule
Input
| Field | Type | Required | Notes |
|---|---|---|---|
report_key | string | yes | Report · max 64 |
rule_id | uuid | yes | Deliver through · id of notification_rule |
frequency | string (enum) | yes | How often · Allowed values: daily, weekly, monthly |
send_hour | number | Hour · Local time in the workspace timezone. · min 0 · max 23 | |
weekday | number | Weekday · 0 = Sunday. Only used for weekly. · min 0 · max 6 | |
window_months | number | Months covered · min 1 · max 24 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
unknown_report | The report must exist in the registry. |
rule_not_found | The notification rule must exist in this workspace. |
rule_not_for_digests | The rule must cover the reports.digest event. |
schedule_exists | The same report is not scheduled twice through one rule at the same frequency. |
Events it publishes: reports.schedule_created
What it writes: report_schedule (insert) — The schedule, with its first send time
curl -X POST https://app.getera.io/api/v1/report_schedules/actions/create_schedule \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"report_key":"Report","rule_id":"NOTIFICATION_RULE_ID","frequency":"daily"}'{
"ok": true,
"result": "…",
"events": [
"reports.schedule_created"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.reportSchedules.createSchedule({
report_key: "Report",
rule_id: "NOTIFICATION_RULE_ID",
frequency: "daily"
})era report_schedules create_schedule \
--report-key "Report" \
--rule-id "NOTIFICATION_RULE_ID" \
--frequency "daily"{
"name": "era__reports__create_schedule",
"arguments": {
"report_key": "Report",
"rule_id": "NOTIFICATION_RULE_ID",
"frequency": "daily"
}
}Errors: 401 no or expired credential · 403 the credential does not hold reports:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_report, rule_not_found, rule_not_for_digests, schedule_exists · 429 slow down
reports.delete_schedule
Stop a scheduled report — Removes the schedule. Digests already sent stay in the notification history.
Needs reports:manage · removes or cannot be undone
https://app.getera.io/api/v1/report_schedules/{id}/actions/delete_schedule- Canonical address:
POST /api/v1/actions/reports.delete_schedule
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Schedule · id of report_schedule |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
schedule_not_found | The schedule must exist in this workspace. |
Events it publishes: reports.schedule_deleted
What it writes: report_schedule (delete) — The schedule row
curl -X POST https://app.getera.io/api/v1/report_schedules/REPORT_SCHEDULE_ID/actions/delete_schedule \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"reports.schedule_deleted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.reportSchedules.deleteSchedule("REPORT_SCHEDULE_ID")era report_schedules delete_schedule REPORT_SCHEDULE_ID{
"name": "era__reports__delete_schedule",
"arguments": {
"id": "REPORT_SCHEDULE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold reports:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 schedule_not_found · 429 slow down
reports.record_export
Record a report export — Adds a row to the export history when somebody downloads a report as CSV.
Needs reports:export · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/report_exports/actions/record_export- Canonical address:
POST /api/v1/actions/reports.record_export
Input
| Field | Type | Required | Notes |
|---|---|---|---|
report_key | string | yes | Report · max 64 |
window_months | number | Window (months) · min 1 · max 24 | |
compare | string | Compared with · max 16 | |
row_count | number | yes | Rows · min 0 · max 10000000 |
filename | string | File name · max 200 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
unknown_report | The report must exist in the registry. |
Events it publishes: reports.exported
What it writes: report_export (insert) — One history row per download
curl -X POST https://app.getera.io/api/v1/report_exports/actions/record_export \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"report_key":"Report","row_count":1}'{
"ok": true,
"result": "…",
"events": [
"reports.exported"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.reportExports.recordExport({
report_key: "Report",
row_count: 1
})era report_exports record_export \
--report-key "Report" \
--row-count 1{
"name": "era__reports__record_export",
"arguments": {
"report_key": "Report",
"row_count": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold reports:export · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_report · 429 slow down
reports.request_export
Export every row — Queues a CSV of every row a report reaches in a period; it is ready to download a moment later.
Needs reports:export · reaches outside (sends, publishes or charges)
https://app.getera.io/api/v1/report_exports/actions/request_export- Canonical address:
POST /api/v1/actions/reports.request_export
Input
| Field | Type | Required | Notes |
|---|---|---|---|
datasetKey | string | yes | Report · max 120 |
filters | object / array | Filters | |
from | date-time | yes | From |
to | date-time | yes | To |
timezone | string | Time zone · max 64 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
dataset_not_found | The report must exist and you must be able to open its module. |
invalid_report_config | Filters must name the report’s own fields, and the period must run forwards. |
Events it publishes: reports.exported
What it writes: report_export (insert) — The request, queued; the worker fills in the file
curl -X POST https://app.getera.io/api/v1/report_exports/actions/request_export \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"datasetKey":"Report","from":"2026-11-02T09:00:00Z","to":"2026-11-02T09:00:00Z"}'{
"ok": true,
"result": "…",
"events": [
"reports.exported"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.reportExports.requestExport({
datasetKey: "Report",
from: "2026-11-02T09:00:00Z",
to: "2026-11-02T09:00:00Z"
})era report_exports request_export \
--dataset-key "Report" \
--from "2026-11-02T09:00:00Z" \
--to "2026-11-02T09:00:00Z"{
"name": "era__reports__request_export",
"arguments": {
"datasetKey": "Report",
"from": "2026-11-02T09:00:00Z",
"to": "2026-11-02T09:00:00Z"
}
}Errors: 401 no or expired credential · 403 the credential does not hold reports:export · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 dataset_not_found, invalid_report_config · 429 slow down
reports.send_digest
Send a scheduled report — Machine-only: the report scheduler runs it when a schedule is due.
Needs reports:manage · reaches outside (sends, publishes or charges)
403 machine_only).https://app.getera.io/api/v1/report_schedules/{id}/actions/send_digest- Canonical address:
POST /api/v1/actions/reports.send_digest
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Schedule · id of report_schedule |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
schedule_not_found | The schedule must exist and be active. |
Events it publishes: reports.digest
What it writes: report_schedule (update) — Stamps the send and moves the next run on
Errors: 401 no or expired credential · 403 the credential does not hold reports:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 schedule_not_found · 429 slow down
Data models
ReportExport
Table report_export · Resource report_exports
GET /api/v1/report_exportsGET /api/v1/report_exports/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
report_key | string · text | |||
format | string · text | |||
window_months | integer · int4 | yes | ||
compare | string · text | yes | ||
row_count | integer · int4 | |||
filename | string · text | yes | ||
exported_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
dataset_key | string · text | yes | ||
query | object · jsonb | yes | ||
period_from | string (date-time) · timestamptz | yes | ||
period_to | string (date-time) · timestamptz | yes | ||
timezone | string · text | yes | ||
status | string · text | queued, running, done, failed | ||
content | string · text | yes | ||
error | string · text | yes | ||
finished_at | string (date-time) · timestamptz | yes |
Primary key: id
Checks:
CHECK ((format = 'csv'::text))CHECK ((row_count >= 0))
ReportSchedule
Table report_schedule · Resource report_schedules
GET /api/v1/report_schedulesGET /api/v1/report_schedules/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
report_key | string · text | |||
rule_id | string (uuid) · uuid | notification_rule | ||
frequency | string · text | daily, weekly, monthly | ||
send_hour | integer · int4 | |||
weekday | integer · int4 | |||
window_months | integer · int4 | |||
active | boolean · bool | |||
next_run_at | string (date-time) · timestamptz | |||
last_sent_at | string (date-time) · timestamptz | yes | ||
created_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((send_hour >= 0) AND (send_hour <= 23)))CHECK (((weekday >= 0) AND (weekday <= 6)))CHECK (((window_months >= 1) AND (window_months <= 24)))
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
reports.digest | A scheduled report digest is ready; delivered only to the rule it names. | schedule_id, report_key, title, route, lines, notificationRuleId |
reports.exported | A report was downloaded as CSV. | export_id, report_key, row_count |
reports.schedule_created | A report was scheduled for delivery. | schedule_id, report_key, frequency |
reports.schedule_deleted | A scheduled report was stopped. | schedule_id, report_key |