History (report)

GET/api/v1/reports/history
Requires anorganization API keywith the scopereports:read

Request

GET/api/v1/reports/history
curl -X GET 'https://app.mailyte.com/api/v1/reports/history' \
  -H 'Authorization: Bearer mk_live_YOUR_API_KEY'
The key names its own organization, so no X-Organization-ID header is needed.

Response

Success.

  • dataobject
    • dataarray<object>
      • objectstringreport_generation
      • idstring

        ULID. `POST /reports/generate` used to call this `report_id` and `GET /reports/history` called it `id`, so following a report you had just queued meant knowing the two were the same field. One name now, and a generate response IS a history row.

      • template_idstringemail-delivery | domain-health | engagement | volume-trends | security

        Matches the `id` of a row from `GET /reports/templates`. Note `security` is accepted here but is not advertised by that endpoint.

      • formatstringpdf | csv | json

        The format you asked for. The job stores a JSON snapshot whichever you ask for -- PDF and CSV rendering is not built -- so this records the request, not what `result` is.

      • statusstringpending | processing | completed | failed

        Poll this. There is deliberately no `estimated_completion`: it was `now() + 30 seconds`, hardcoded and unrelated to the queue depth, the job or the report's size, so polling against it polled at the wrong time.

      • parametersobject

        What the report was queued with. Always an object: it is an array cast, so it used to serialise as `[]` when empty and `{}` otherwise, and a field whose JSON type changes with its contents is unusable from a typed language.

      • errorstring

        Why the build failed. Non-null only when `status` is `failed`. NULL MEANS NONE -- there is no error -- not that we lost the reason: the only two writers (`GenerateReportJob::handle()` and its `failed()` hook) set `status` and `error` in the same update, so a `failed` row always carries one and no other status ever does.

      • resultany

        The stored report, rendered through the SAME resource the live endpoint uses, so the vocabulary is identical whether you read a report live or out of history. Returning the raw snapshot would have re-published `total_volume`, `bounce_rate`, `total_sent` and the rest through the back door. Null while the job is queued, when it failed, and for a template we have no resource for. Its `period` is always null -- a snapshot keeps no record of the window it covered.

      • requested_atstring

        When the build was ASKED for (`created_at`), which is what a caller polling a queue cares about. NULL MEANS UNKNOWN -- we do not know when it was requested -- and never "it has not been requested yet": the row exists only because somebody requested it. `report_generations.created_at` is a NULLABLE timestamp column (Laravel's `timestamps()` default), so a row written without touching the timestamps has none. Do not sort a queue on this field without handling the null.

      • completed_atstring

        When the build finished, and it is only ever written on SUCCESS. Read it against `status`: while `pending` or `processing`, null means NOT YET; when `failed`, null means NONE and it will stay null forever, because the job's `failed()` hook sets `status` and `error` and never a finish time. So a null here is not a way to tell a running build from a dead one -- `status` is.

    • pagination_metaany

Returned inside the standard envelope.

Errors

StatusWhen
401The API key is missing, unknown, revoked or expired. All four answer identically, on purpose: distinguishing them would confirm which keys exist.
403The key is valid but may not do this: it lacks the required scope, its IP allowlist does not include you, or this endpoint does not accept API keys.
404No such resource in this organization.
422The request was understood but the values were not acceptable.
429Too many requests, or the organization has spent its sending allowance. `Retry-After` says how long to wait.

Every status, with what causes it and what to do, is on the error reference.