> ## Documentation Index
> Fetch the complete documentation index at: https://omni.fireflo.au/llms.txt
> Use this file to discover all available pages before exploring further.

# GET /v1/reports/{report_id}

> One report's figures — columns, rows and totals — for its own period or another, as JSON or CSV.

Run a report from [GET /v1/reports](/api-reference/reports/list): its columns, rows and
totals, for the period it is saved with or one you ask for. Days are the account's, in
its time zone.

<Note>Needs the `reports:read` scope, and Analytics on the account.</Note>

## Path and query parameters

| Field | Type | Required | Notes |
| :- | :- | :- | :- |
| `report_id` | string | Yes | A built-in report's name (`messages.delivery`) or a shared report's UUID. |
| `preset` | string | No | A period: `today`, `yesterday`, `7d`, `30d`, `90d`, `this_week`, `last_week`, `this_month`, `last_month`, `this_year`, `12m`. |
| `from` | string | No | Instead of `preset`: the first day, `YYYY-MM-DD`. |
| `to` | string | No | With `from`: the last day, included. Up to 800 days. |
| `compare` | string | No | `true` adds the period before, of the same length, and each total's change; `false` leaves them out. Default: as the report is saved. |
| `format` | string | No | `json` (the default) or `csv`, for a file. |

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.fireflo.au/v1/reports/messages.delivery?preset=last_month" \
    -H "Authorization: Bearer $OMNI_API_KEY"
  ```

  ```python Python theme={null}
  import os

  import requests

  response = requests.get(
      "https://api.fireflo.au/v1/reports/messages.delivery",
      headers={"Authorization": f"Bearer {os.environ['OMNI_API_KEY']}"},
      params={"preset": "last_month"},
  )
  print(response.status_code, response.json())
  ```

  ```javascript Node theme={null}
  const response = await fetch("https://api.fireflo.au/v1/reports/messages.delivery?preset=last_month", {
    headers: { Authorization: `Bearer ${process.env.OMNI_API_KEY}` },
  });
  console.log(response.status, await response.json());
  ```
</CodeGroup>

## Response

`200 OK` — `columns` describe each row's fields: the groups first, then the measures with
their `format` (as in [GET /v1/reports/sources](/api-reference/reports/sources)). Group
values are written as people read them (`"WhatsApp"`, `"2026-09-01"`); a rate is a
fraction; money is in minor units of `currency`; a duration is in seconds; a measure with
nothing to count is `null`. A report grouped by something other than time keeps its top
rows and adds up the rest in one row named `Other`. `cut` is true when more than 10,000
rows were found and only those were counted. `previous` and `change` are there when the
report compares; a change is a fraction (`0.124` is +12.4%).

With `format=csv` the answer is a CSV file instead: a header row, the rows and a totals
row, with rates as percentages and money in whole units.

```json theme={null}
{
  "title": "Delivery and read rate by channel",
  "source": "messages",
  "period": { "from": "2026-09-01", "to": "2026-09-30", "label": "Last month" },
  "columns": [
    { "key": "channel", "label": "Channel", "kind": "category" },
    { "key": "sent", "label": "Sent", "kind": "measure", "format": "number" },
    { "key": "delivered", "label": "Delivered", "kind": "measure", "format": "number" },
    { "key": "delivery_rate", "label": "Delivery rate", "kind": "measure", "format": "percent" },
    { "key": "read_rate", "label": "Read rate", "kind": "measure", "format": "percent" }
  ],
  "rows": [
    { "channel": "SMS", "sent": 13420, "delivered": 12301, "delivery_rate": 0.9166, "read_rate": 0 },
    { "channel": "WhatsApp", "sent": 6410, "delivered": 6188, "delivery_rate": 0.9654, "read_rate": 0.8121 }
  ],
  "totals": { "sent": 19830, "delivered": 18489, "delivery_rate": 0.9324, "read_rate": 0.2718 },
  "chart": "bar",
  "cut": false,
  "currency": "INR",
  "previous": {
    "period": { "from": "2026-08-02", "to": "2026-08-31", "label": "Previous" },
    "totals": { "sent": 17640, "delivered": 16512, "delivery_rate": 0.9361, "read_rate": 0.2604 }
  },
  "change": { "sent": 0.1241, "delivered": 0.1197, "delivery_rate": -0.004, "read_rate": 0.0438 }
}
```

## Errors

| Status | Error code | When |
| :- | :- | :- |
| 400 | `invalid_request` | The period isn't one (`field` is `period`), or `format` isn't json or csv. |
| 403 | `scope_missing` | The key doesn't have the `reports:read` scope. |
| 404 | `not_found` | No built-in or shared report has that id. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.