Worked usage samples
The chain below covers read the aggregate → page its window → stand up an alert rule → branch on the errors. The aggregate (GET /api/v1/messages/usage/records) is the Twilio-parity surface that rolls every billable row up per channel and direction with cost attribution — the exact numbers the usage-series and usage-records dashboard pages render. The cursor-windowed row-level feed behind the same pages (since/until/channel/cursor) lives on the usage records overlay; this page adds the aggregate and its alerting sibling.
1. Read the per-channel usage aggregate
GET /api/v1/messages/usage/records?days=30sms-inbound, sms-outbound, mms-inbound, mms-outbound, voice-inbound, voice-outbound) over a trailing window. days (1–365, default 30) sizes the window; SMS counts by segment, MMS and voice by message. Currency stays in the group key, so a mid-window currency flip returns one record per currency bucket rather than a mixed sum. count and price are the cost-attribution pair — price is the debited amount in currency.
country_splits=1 (also accepts true / yes) to extend each record with a countries array — a per-ISO-3166 alpha-2 breakdown of country, count, and price for that category, derived from the recipient’s E.164. Pass a smaller or larger days to re-slice the window; there is no cursor here because the aggregate returns its full category set in one body. The row-level counterpart with since/until/channel/cursor pagination is documented on the usage records page.
2. Stand up an alert rule on the same signal
POST /api/v1/usage/alert-rulessms_delivery_rate) and volume/spend (outbound_message_volume, spend). Create at most 50 rules per organization; a threshold rule needs a comparator (gt / gte / lt / lte) and a finite threshold on the metric’s native unit — percent, count, or USD.
GET /api/v1/usage/alert-rules/events and the in-app notification dropdown. POST /api/v1/usage/alert-rules/evaluate runs the evaluator for your organization now instead of waiting for the next sweep.
3. Error branches
Two branches decide what callers do next on this surface. Validation — a malformed rule body.POST /api/v1/usage/alert-rules validates the body before anything persists. A threshold rule missing its comparator or threshold, a metric outside the registry (sms_delivery_rate, outbound_message_volume, spend), or a window_days above 30 returns 422 VALIDATION_ERROR naming the rejected field:
422
messages needs a key minted with messages:read (or messages:write), and the /usage/* reads need usage:read. A dashboard session without an accepted org role, or an API key without the scope, is rejected 403 before any query runs:
403
422, and re-mint the key with the missing scope or assign an accepted role before retrying a 403. The envelope mechanics — error.code / status / details, the retry-vs-terminal decision table — live in the error-handling guide; only the usage-specific triggers above belong here.