Reports
A report is one file Aleta produced: a report the legacy customer portal generated (source: "customer_portal"), a report the legacy blob service holds (source: "legacy_blob"), a file a report subscription rendered (source: "report_subscription"), or a file you asked for with Create a report (source: "report_run"). Reading requires the reports:read scope and the reports module at read. You see the reports of the clients you see; a report for a lens also needs that lens to be one you see.
The report object
idstringStable identifier for the report. A UUID — treat it as opaque.
sourcestringcustomer_portal,legacy_blob,report_subscriptionorreport_run.originstringlegacy(made on the legacy platform:customer_portalandlegacy_blob) orv1.namestringThe report's name: the legacy portal's English name, or the rendered file's name.
name_keystring or nullThe legacy localisation key (
report_name.*). Legacy reports only.client_idstring or nullThe client the report is filed under.
lens_idstring or nullThe lens the report is for, when it has one. A rendered file is for its subscription line's lens, or else its report definition's lens.
report_definition_idstring or nullThe report definition a subscription rendered.
legacy_idstring or nullThe id the legacy portal showed, as a decimal string. Customer-portal reports only.
report_subscription_idstring or nullThe subscription that rendered the file.
job_idstring or nullThe job that rendered a
report_run.as_of_datestring or nullThe reporting date,
YYYY-MM-DD.nullfor a subscription render stored before renders recorded it.periodstring or nulldaily,monthly,quarterly,half_yearlyoryearly.groupstring or nulldaily,risk,performance,controlling,esgoraccounting.formatstringpdf,xlsx,csvorother.created_atstringWhen the file was made, ISO 8601 in UTC.
List reports
Returns reports newest first (created_at, then id) in the standard v1 list envelope. A legacy blob report is listed in its newest version only, as the legacy portal listed it. An unknown source, to_date before from_date, or any parameter not listed here is a 400.
Query parameters
client_idstringOnly this client's reports.
lens_idstringOnly this lens's reports.
sourcestringcustomer_portal,legacy_blob,report_subscriptionorreport_run.legacy_idstringOnly the customer-portal report with this legacy id.
from_datestringReporting date on or after this day,
YYYY-MM-DD. A report without a reporting date is left out by a date filter.to_datestringReporting date on or before this day.
limitintegerPage size. Defaults to 100, capped at 1 000.
cursorstringOpaque cursor from the previous page's
next_cursor. Omit for the first page.
curl -G https://api.aleta.ai/v1/reports \
-H "Authorization: Bearer {access_token}" \
-d source=customer_portal
{
"data": [
{
"id": "0190f5f1-9f8e-7d6c-8b5a-493827161504",
"source": "customer_portal",
"origin": "legacy",
"name": "Monthly report",
"name_key": "report_name.m10a",
"client_id": "0190f5e0-1111-7aaa-8bbb-0123456789ab",
"lens_id": "0190f5e0-2222-7aaa-8bbb-0123456789ab",
"report_definition_id": null,
"legacy_id": "48211",
"report_subscription_id": null,
"job_id": null,
"as_of_date": "2026-06-30",
"period": "monthly",
"group": "performance",
"format": "pdf",
"created_at": "2026-07-01T06:00:00.104000Z"
}
],
"next_cursor": null,
"limit": 100
}
Get a report
Returns one report. A report you do not see, or an id that is not a UUID, is a 404.
curl https://api.aleta.ai/v1/reports/0190f5f1-9f8e-7d6c-8b5a-493827161504 \
-H "Authorization: Bearer {access_token}"
Download a report
Returns the report's file: 200 with the bytes, the file's Content-Type, Content-Length when the storage reports it, Content-Disposition with the file's name, and Cache-Control: no-store, private. Requires reports:read; the same rules as Get a report decide whether you may read it. No storage URL is ever returned. Every download is recorded in your audit trail.
Query parameters
dispositionstringattachment(the default: save the file) orinline(show it in the browser). Any other value, or any other parameter, is a400.
The file is served from Aleta's storage in your tenant's residency. A legacy report whose file has not been moved to Aleta's storage, and a file missing from Aleta's storage, are a 404. A file in Aleta's storage larger than 32 MiB is a 422 with code file_too_large. While the storage is not configured the route answers 503.
curl -OJ https://api.aleta.ai/v1/reports/0190f5f1-9f8e-7d6c-8b5a-493827161504/download \
-H "Authorization: Bearer {access_token}"
Create a report
Renders one of your report definitions (/v1/report-definitions) once, for a lens or a client, as of a day, and answers 202 Accepted with a job and Location: /v1/jobs/{job_id} (also on a replay, not on a dry run). Follow it with GET /v1/jobs/{job_id} (needs platform:read); when it has succeeded, the file is in the list with source: "report_run" and this job_id, and can be downloaded. Nothing is mailed. Requires reports:write and the reports module at read_write. Takes ?dry_run=true and Idempotency-Key like every v1 write.
You also need a grant on the whole client of the scope: on the tenant, on the client (a read-only client grant counts), or as the client's advisor. For a lens, that is the lens's client. A grant on one entity or account, or a shared lens, is not enough: the answer is 403, because the report shows the whole client.
Body
report_definition_idstringRequired. One of your own definitions; copy an Aleta definition with
copy_fromfirst. It must not be archived.lens_idstringThe lens to report on. Send exactly one of
lens_idandclient_id; when the definition has a client, the lens or client must belong to it.client_idstringThe client to report on.
formatstringRequired.
pdforcsv;csvneeds a definition with exactly one bound data table.xlsxis not offered.as_of_datestringThe reporting date,
YYYY-MM-DD. Defaults to, and may not be after, the day the scope's data is released through (data_ready_throughinGET /v1/data-status). The definition's period (for example the last full month) resolves as a scheduled delivery for that date would; a definition created withPOST /v1/report-definitionscovers the year to date.currencystringAn ISO 4217 code; the definition's currency when absent.
languagestringdaoren. Only a definition with translations changes language; its authored text stays as written otherwise.
Every refusal is a 400 with the reason: an unknown field, a format, language, currency or as_of_date not as described, both or neither scope, a scope you do not see, an unknown or archived definition, a scope outside the definition's client, csv on a definition that cannot render it, a date after the released day, or a scope with no released data yet. The rendering reads no commentary, so a run records none. A caller without a grant on the whole client gets 403 (see above).
curl -X POST https://api.aleta.ai/v1/reports \
-H "Authorization: Bearer {access_token}" \
-H "Idempotency-Key: q3-pack-lens-a" \
-H "Content-Type: application/json" \
-d '{"report_definition_id": "0190f5e0-4444-7aaa-8bbb-0123456789ab", "lens_id": "0190f5e0-2222-7aaa-8bbb-0123456789ab", "format": "pdf", "as_of_date": "2026-09-30"}'
{
"job_id": "0190f5f3-5c6d-7e8f-9a0b-1c2d3e4f5a6b",
"status": "queued",
"report_definition_id": "0190f5e0-4444-7aaa-8bbb-0123456789ab",
"lens_id": "0190f5e0-2222-7aaa-8bbb-0123456789ab",
"client_id": "0190f5e0-1111-7aaa-8bbb-0123456789ab",
"format": "pdf",
"as_of_date": "2026-09-30",
"currency": null,
"language": null,
"created_at": "2026-10-18T09:00:00.123456Z"
}