# Chart campaign performance over time

`GET /v1/campaigns/{id}/series`

Requires an API key with the `campaigns:read` scope. [About keys and scopes](/docs/api/authentication)

Counts of each delivery and engagement event per hour or per day in the workspace's timezone (`timezone`), split by channel. An event lands in the bucket in which it happened: an open at 10:05 counts in the 10:00 hour. Opens and clicks are the first per message, as in the stats. Buckets start at the first send and run without gaps to the current one, up to 72 hours or 366 days; events after that are outside the series. A campaign that has not sent returns no points. Each bucket counts events, not a cohort, so a per-bucket open rate means nothing — divide running totals instead.

## Path parameters

- `id` (string, required, uuid)

## Query parameters

- `interval` (string, required, one of `hour`, `day`) — The bucket size.

## Returns

Returns `200 OK` with a `CampaignSeries` in `data`. Otherwise returns an error; [Errors](/docs/api/errors) lists every code and which to retry.

- `campaignId` (string, required, uuid)
- `interval` (string, required, one of `hour`, `day`) — The bucket size.
- `timezone` (string | null, required) — The workspace timezone buckets follow; `null` when the campaign has not sent.
- `points` (object[], required, at most 366 items) — One point per bucket, from the first send to the current one.
  - `date` (string, required, date-time) — Start of the bucket: a local hour or midnight in `timezone`, as an instant.
  - `byChannel` (object, required) — Event counts per channel; a channel the campaign skips is absent.
    - `email` (object, optional)
      - `sent` (integer, required, min 0, max 9007199254740991)
      - `delivered` (integer, required, min 0, max 9007199254740991)
      - `opened` (integer, required, min 0, max 9007199254740991)
      - `clicked` (integer, required, min 0, max 9007199254740991)
      - `bounced` (integer, required, min 0, max 9007199254740991)
      - `complained` (integer, required, min 0, max 9007199254740991)
      - `unsubscribed` (integer, required, min 0, max 9007199254740991)
    - `push` (object, optional)
      - `sent` (integer, required, min 0, max 9007199254740991)
      - `failed` (integer, required, min 0, max 9007199254740991)
      - `opened` (integer, required, min 0, max 9007199254740991)
      - `clicked` (integer, required, min 0, max 9007199254740991)

## Example

```bash
curl 'https://api.kirora.io/v1/campaigns/018f2a4e-6b1c-7d3e-9f40-37386ae05a6b/series?interval=hour' \
  -H "Authorization: Bearer $KIRORA_API_KEY"
```

Response `200`:

```json
{
  "data": {
    "campaignId": "018f2a4e-6b1c-7d3e-9f40-3478765e5a6b",
    "interval": "hour",
    "timezone": "Europe/London",
    "points": [
      {
        "date": "2030-03-14T09:30:00Z",
        "byChannel": {}
      }
    ]
  }
}
```
