<Intro>

<EndpointHeader />

<Description>

Returns one row of aggregate SMS statistics per hour for the workspace -- per UTC hour by default, or per local hour when you set `timezone` (so a timezone with a sub-hour offset gets correctly aligned hours). Useful for inspecting send rate and deliverability inside a single day or across a recent window.
Rows are bucketed by send time, not event time -- a delivery confirmation recorded at 14:07 for a message accepted at 09:00 lands in the 09:00 row, alongside that message's own `accepted`. A recent row therefore under-reports `delivered` while its delivery reports are still arriving, and its counts only ever grow toward the truth.
Each row carries lifecycle counts only (accepted, sent, delivered, undelivered, failed, rejected, expired); rates and latency are whole-window aggregates available from the summary endpoint.
A single request may span at most 30 days (720 hourly rows). For longer ranges use the daily endpoint, which has a 365-day window. Requesting an hourly window longer than 30 days, or a `from` after `to`, returns 422. `from` and `to` are interpreted as instants (ISO 8601); the server rounds each down to the enclosing hour and echoes the rounded values back in `period`. Both bounds are inclusive after round-down.

</Description>

</Intro>

<Parameters in="query">

<Parameter name="from" type="string">

<Description>

Start of the window (ISO 8601 instant). Rounded down to the start of its hour -- the local hour when `timezone` is set, otherwise the UTC hour -- and that hour is included. When `timezone` is set, a numeric UTC offset here (for example `+05:45`) is rejected; use a `Z` (UTC) instant. Defaults to 7 days before `to` when omitted.

</Description>

</Parameter>

<Parameter name="to" type="string">

<Description>

End of the window (ISO 8601 instant). Rounded down to the start of its hour -- the local hour when `timezone` is set, otherwise the UTC hour -- and that hour is included (both bounds inclusive). When `timezone` is set, a numeric UTC offset here is rejected; use a `Z` (UTC) instant. Defaults to the current hour when omitted. Window may not exceed 30 days (720 hours).

</Description>

</Parameter>

<Parameter name="timezone" type="string">

<Description>

IANA timezone identifier (for example `Asia/Kathmandu`) to report in; defaults to UTC. Day and hour boundaries and the default window when `from` and `to` are omitted both follow it, so a calendar-day `from` or `to` names a local day. A `from` or `to` carrying its own UTC offset is rejected while this is set: pass a calendar day or a `Z` instant.

</Description>

</Parameter>

<Parameter name="originator" type="string">

<Description>

Restrict the statistics to a single originator (the sender address messages were sent from). Mutually exclusive with the other dimension filters (`country`, `category`, `carrier`); only one may be set per request. Matches the message `from`.

</Description>

</Parameter>

<Parameter name="country" type="string">

<Description>

Restrict the statistics to a single destination country, as an ISO 3166-1 alpha-2 code. Mutually exclusive with the other dimension filters (`originator`, `category`, `carrier`); only one may be set per request.

</Description>

</Parameter>

<Parameter name="category" type="string">

<Description>

Restrict the statistics to a single category. Mutually exclusive with the other dimension filters (`originator`, `country`, `carrier`); only one may be set per request.

</Description>

</Parameter>

<Parameter name="carrier" type="string">

<Description>

Restrict the statistics to a single delivery carrier. Mutually exclusive with the other dimension filters (`originator`, `country`, `category`); only one may be set per request.

</Description>

</Parameter>

</Parameters>

<Payload kind="response">

<Field name="period" type="object" required>

<Description>

The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to.

</Description>

<FieldChildren kind="response">

<Field name="from" type="string" prefix="period." required>

<Description>

Inclusive start of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain.

</Description>

</Field>

<Field name="to" type="string" prefix="period." required>

<Description>

Inclusive end of the window. A calendar day (YYYY-MM-DD) on the day grain, an RFC 3339 instant rounded to the hour on the hour grain.

</Description>

</Field>

<Field name="grain" type="string" prefix="period." required>

<Description>

The bucket grain of the series, either `day` or `hour`.

</Description>

<Description>

Possible values: `day`, `hour`

</Description>

</Field>

<Field name="data_as_of" type="nullable string" prefix="period.">

<Description>

The instant the statistics in this response are current to: events recorded up to roughly this time are reflected, while more recent events may not be yet. Statistics are served from a rolling aggregation that refreshes every few seconds, so a response is near-real-time but not live; use this field to label data freshness rather than assuming the numbers are to-the-second. Null when the freshness boundary is not being reported.

</Description>

</Field>

</FieldChildren>

</Field>

<Field name="data" type="array of object" required>

<Description>

One row per bucket (day or hour, per the grain) in the period, in chronological order. Zero-filled -- buckets with no activity are included with zero counts, so the series charts continuously without client-side gap handling.

</Description>

<FieldChildren kind="response">

<Field name="bucket" type="string" prefix="data." required>

<Description>

The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain.

</Description>

</Field>

<Field name="delivery" type="object" prefix="data." required>

<FieldChildren kind="response">

<Field name="accepted" type="integer" prefix="data.delivery." required>

<Description>

Distinct messages accepted for sending after admission checks.

</Description>

</Field>

<Field name="sent" type="integer" prefix="data.delivery." required>

<Description>

Distinct messages handed off to the carrier for delivery.

</Description>

</Field>

<Field name="delivered" type="integer" prefix="data.delivery." required>

<Description>

Distinct messages the carrier confirmed as delivered to the handset.

</Description>

</Field>

<Field name="undelivered" type="integer" prefix="data.delivery." required>

<Description>

Distinct messages the carrier reported as not delivered.

</Description>

</Field>

<Field name="failed" type="integer" prefix="data.delivery." required>

<Description>

Distinct messages that failed during sending.

</Description>

</Field>

<Field name="rejected" type="integer" prefix="data.delivery." required>

<Description>

Distinct messages rejected before any send attempt, for example by sending policy or a message-generation failure.

</Description>

</Field>

<Field name="expired" type="integer" prefix="data.delivery." required>

<Description>

Distinct messages that could not be delivered within their validity window and expired.

</Description>

</Field>

</FieldChildren>

</Field>

</FieldChildren>

</Field>

</Payload>