openapi: 3.2.0 info: title: Bird Sms Stats API version: 1.0.0 description: 'The Bird API: one REST API for email, SMS, WhatsApp, verification, and Realtime.' servers: - url: https://{region}.platform.bird.com description: 'Regional API endpoint. Use the host for the region your organization is hosted in. Official Bird SDKs and the CLI select it automatically from your API key, so you rarely need to set it by hand. ' variables: region: default: us1 enum: - us1 - eu1 description: The region your organization's data is hosted in. - url: https://platform.bird.com description: Region-independent endpoint for authentication and account administration. - url: http://localhost:8080 description: Local development. security: - BearerAuth: [] tags: - name: sms-stats description: SMS analytics, including daily and hourly lifecycle counts, dimension breakdowns, and a KPI summary. paths: /v1/sms/stats/summary: get: operationId: getSMSStatsSummary summary: Get aggregate outbound SMS statistics description: 'Returns one aggregate row for the requested period. It includes SMS lifecycle counts, delivery and failure rates, and processing, delivery, and total latency percentiles (`p50`, `p95`, and `p99`). Rows use send-time attribution, so recent periods can under-report `delivered` while delivery reports arrive. Rate fields are `null` when their denominator is zero. For example, `delivery_rate` is `null` when no message was accepted. `from` and `to` must both be days or RFC 3339 instants. Day windows cover up to 365 days. Instant bounds round down to the hour and may span up to 720 hours. Mixing the forms returns `422`. Set `timezone` for local boundaries, one dimension filter at most, or `compare=previous_period` for the preceding equal-length window.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.summary security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: 'Inclusive start of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `to`. When omitted, it defaults to 30 days before `to` for day windows or 168 hours (7 days) before `to` for hour windows. ' schema: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ example: '2026-05-01' - name: to in: query required: false description: 'Inclusive end of the window: a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The `timezone` parameter makes a calendar day local and rounds an instant down to the local hour. Omit `timezone` to use UTC. When `timezone` is set, a numeric UTC offset is rejected; use a calendar day or a `Z` (UTC) instant. This value must use the same form as `from`. When omitted, it defaults to today for day windows or the current hour for hour windows in that timezone. Day windows may not exceed 365 days; hour windows may not exceed 720 hours (30 days). ' schema: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: originator in: query required: false 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`. ' schema: type: string minLength: 1 example: BirdSMS - name: country in: query required: false 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. ' schema: type: string minLength: 1 example: US - name: category in: query required: false 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. ' schema: type: string minLength: 1 example: transactional - name: carrier in: query required: false 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. ' schema: type: string minLength: 1 example: Verizon - name: compare in: query required: false description: 'Set to `previous_period` to also include the same statistics for the immediately preceding window of equal length, plus the change between the two, so you can show "+X% vs last period" without a second request. ' schema: $ref: '#/components/schemas/StatsComparePeriod' responses: '200': description: Aggregate summary for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsSummary' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/daily: get: operationId: getSMSStatsDaily summary: Get daily outbound SMS statistics description: 'Returns one row of SMS lifecycle counts per calendar day. Rows use send-time attribution, so a delivery confirmation is counted on the day when its message was accepted. Recent rows can under-report `delivered` while delivery reports arrive. Days without activity contain zero counts. Rates and latency are whole-window aggregates available from the summary endpoint. Use the message detail endpoints for individual message status. A request may span up to 365 days; a longer window returns `422`. Set `timezone` for local calendar days instead of UTC.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.daily security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: originator in: query required: false 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`. ' schema: type: string minLength: 1 example: BirdSMS - name: country in: query required: false 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. ' schema: type: string minLength: 1 example: US - name: category in: query required: false 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. ' schema: type: string minLength: 1 example: transactional - name: carrier in: query required: false 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. ' schema: type: string minLength: 1 example: Verizon responses: '200': description: Daily aggregate stats for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/hourly: get: operationId: getSMSStatsHourly summary: Get hourly outbound SMS statistics description: 'Returns one row of SMS lifecycle counts per hour. Rows use send-time attribution, so a delivery confirmation is counted in the hour when its message was accepted. Recent rows can under-report `delivered` while delivery reports arrive. Rates and latency are whole-window aggregates available from the summary endpoint. Set `timezone` for local hours instead of UTC, including zones with sub-hour offsets. A request may span up to 30 days (720 rows). `from` and `to` are ISO 8601 instants; each bound rounds down to the hour and remains inclusive. An excessive or reversed window returns `422`. Use the daily endpoint for longer ranges.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.hourly security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start of the window (ISO 8601 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise. When `timezone` is set, a numeric UTC offset such as `+05:45` is rejected; use a `Z` (UTC) instant. Defaults to 7 days before `to` when omitted. schema: type: string format: date-time example: '2026-05-25T00:00:00Z' - name: to in: query required: false description: End of the window (ISO 8601 instant), rounded down to the start of its hour and included. The boundary uses the local hour when `timezone` is set and the UTC hour otherwise, so both bounds are inclusive. When `timezone` is set, a numeric UTC offset is rejected; use a `Z` (UTC) instant. Defaults to the current hour when omitted. The window may not exceed 30 days (720 hours). schema: type: string format: date-time example: '2026-05-25T23:59:59Z' - $ref: '#/components/parameters/StatsTimezone' - name: originator in: query required: false 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`. ' schema: type: string minLength: 1 example: BirdSMS - name: country in: query required: false 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. ' schema: type: string minLength: 1 example: US - name: category in: query required: false 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. ' schema: type: string minLength: 1 example: transactional - name: carrier in: query required: false 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. ' schema: type: string minLength: 1 example: Verizon responses: '200': description: Hourly aggregate stats for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/originators: get: operationId: getSMSStatsByOriginator summary: Get outbound SMS statistics by originator description: 'Returns aggregate delivery and latency stats grouped by originator (the sender address messages were sent from) for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare sending performance across the senders you dispatch from. Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; requesting a longer range returns 422.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.by_originator security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: sort in: query required: false description: 'Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`. ' schema: $ref: '#/components/schemas/SMSStatsSortMetric' - name: limit in: query required: false description: Maximum number of originator rows to return, ranked by the `sort` field descending. schema: type: integer minimum: 1 maximum: 200 default: 50 - name: include_trend in: query required: false description: 'When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422. ' schema: type: boolean default: false - name: trend_grain in: query required: false description: Bucket grain for the `trend` series. Has no effect unless `include_trend=true`. schema: $ref: '#/components/schemas/StatsTrendGrain' responses: '200': description: Per-originator breakdown for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsByOriginatorResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/countries: get: operationId: getSMSStatsByCountry summary: Get outbound SMS statistics by country description: 'Returns aggregate delivery and latency stats grouped by destination country for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare sending performance across the countries you send to. Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; requesting a longer range returns 422.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.by_country security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: sort in: query required: false description: 'Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`. ' schema: $ref: '#/components/schemas/SMSStatsSortMetric' - name: limit in: query required: false description: Maximum number of country rows to return, ranked by the `sort` field descending. schema: type: integer minimum: 1 maximum: 200 default: 50 - name: include_trend in: query required: false description: 'When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422. ' schema: type: boolean default: false - name: trend_grain in: query required: false description: Bucket grain for the `trend` series. Has no effect unless `include_trend=true`. schema: $ref: '#/components/schemas/StatsTrendGrain' responses: '200': description: Per-country breakdown for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsByCountryResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/categories: get: operationId: getSMSStatsByCategory summary: Get outbound SMS statistics by category description: 'Returns aggregate delivery and latency stats grouped by message category for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare sending performance across the categories you send under. Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; requesting a longer range returns 422.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.by_category security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: sort in: query required: false description: 'Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`. ' schema: $ref: '#/components/schemas/SMSStatsSortMetric' - name: limit in: query required: false description: Maximum number of category rows to return, ranked by the `sort` field descending. schema: type: integer minimum: 1 maximum: 200 default: 50 - name: include_trend in: query required: false description: 'When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422. ' schema: type: boolean default: false - name: trend_grain in: query required: false description: Bucket grain for the `trend` series. Has no effect unless `include_trend=true`. schema: $ref: '#/components/schemas/StatsTrendGrain' responses: '200': description: Per-category breakdown for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsByCategoryResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/error-codes: get: operationId: getSMSStatsByErrorCode summary: Get outbound SMS statistics by error code description: 'Returns aggregate delivery and latency statistics grouped by normalized failure reason for the requested period. The grouping key matches the `error_code` filter on the message list, so each row maps directly to the affected messages rather than a raw carrier code. Rows are ranked by the `sort` metric (default `failed`) descending and capped at the requested `limit` (default 50, hard maximum 200). Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; requesting a longer range returns 422.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.by_error_code security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: sort in: query required: false description: 'Metric to rank rows by, applied descending. Defaults to `failed`. Only lifecycle counts are sortable; this breakdown has no rates. ' schema: $ref: '#/components/schemas/SMSStatsLifecycleSortMetric' - name: limit in: query required: false description: Maximum number of error-code rows to return, ranked by the `sort` field descending. schema: type: integer minimum: 1 maximum: 200 default: 50 - name: include_trend in: query required: false description: 'When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422. ' schema: type: boolean default: false - name: trend_grain in: query required: false description: Bucket grain for the `trend` series. Has no effect unless `include_trend=true`. schema: $ref: '#/components/schemas/StatsTrendGrain' responses: '200': description: Per-error-code breakdown for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsByErrorCodeResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/carriers: get: operationId: getSMSStatsByCarrier summary: Get outbound SMS statistics by carrier description: 'Returns aggregate delivery and latency stats grouped by delivery carrier for the requested period. Rows are ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare delivery performance across the carriers that handled your messages. Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving, and its counts grow as reports arrive. The maximum window is 365 days; requesting a longer range returns 422.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.by_carrier security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: sort in: query required: false description: 'Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`. ' schema: $ref: '#/components/schemas/SMSStatsSortMetric' - name: limit in: query required: false description: Maximum number of carrier rows to return, ranked by the `sort` field descending. schema: type: integer minimum: 1 maximum: 200 default: 50 - name: include_trend in: query required: false description: 'When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422. ' schema: type: boolean default: false - name: trend_grain in: query required: false description: Bucket grain for the `trend` series. Has no effect unless `include_trend=true`. schema: $ref: '#/components/schemas/StatsTrendGrain' responses: '200': description: Per-carrier breakdown for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsByCarrierResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/tags: get: operationId: getSMSStatsByTag summary: Get outbound SMS statistics by tag description: 'Returns delivery and latency statistics grouped by tag (`name:value`). Rows sort by the selected metric in descending order and are capped by `limit`. The default sort is `accepted`; the default limit is 50 and the maximum is 200. Only tagged messages appear. A message with several tags is counted once under each, so rows do not sum to the period total. Rows use send-time attribution, so recent periods can under-report `delivered` while delivery reports arrive. A request may span up to 365 days; a longer window returns `422`.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.by_tag security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted; with `include_trend=true` and `trend_grain=hourly` the default tightens to keep the window within the 720-hour trend cap. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: sort in: query required: false description: 'Metric to rank rows by, applied descending. Any lifecycle count or derived rate in the response may be used; rows whose rate is undefined (zero denominator) sort last. Defaults to `accepted`. ' schema: $ref: '#/components/schemas/SMSStatsSortMetric' - name: limit in: query required: false description: Maximum number of tag rows to return, ranked by the `sort` field descending. schema: type: integer minimum: 1 maximum: 200 default: 50 - name: include_trend in: query required: false description: 'When true, each row also carries a `trend` array: a short per-bucket lifecycle-count series for that row over the window. Returned only when `limit` is 50 or fewer and the window is at most 90 days (trend_grain=daily) or 720 hours (trend_grain=hourly); a larger request returns 422. ' schema: type: boolean default: false - name: trend_grain in: query required: false description: Bucket grain for the `trend` series. Has no effect unless `include_trend=true`. schema: $ref: '#/components/schemas/StatsTrendGrain' responses: '200': description: Per-tag breakdown for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsByTagResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/statuses: get: operationId: getSMSStatsByStatus summary: Get outbound SMS statistics by status description: 'Returns one row per lifecycle status with activity in the requested period, ordered by count descending. The statuses are `accepted`, `sent`, `delivered`, `undelivered`, `failed`, `rejected`, and `expired`. Rows use send-time attribution. A delivery confirmed during the period for a message accepted earlier counts against the earlier period. A recent period therefore under-reports `delivered` while delivery reports are still arriving. With at most seven statuses, this breakdown has no cap, ranking, limit, or trend parameters. The maximum window is 365 days; requesting a longer range returns 422.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.by_status security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive) in YYYY-MM-DD, interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' responses: '200': description: Per-status breakdown for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSStatsByStatusResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/inbound/summary: get: operationId: getSMSInboundStatsSummary summary: Get aggregate inbound SMS statistics description: 'Returns the total number of messages your numbers received over the period, using the time the carrier received each message. The response contains only a count because a received message has one state. Use the outbound statistics endpoints for delivery rates and latency data about messages you send. The maximum window is 365 days; a longer range returns 422. Set `timezone` to resolve the period against your local calendar instead of UTC.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.inbound.summary security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: 'Inclusive start of the window, either a calendar day (YYYY-MM-DD) or an RFC 3339 instant rounded down to the hour. The form you use selects the grain the total is resolved at. Interpreted in `timezone`, or in UTC when `timezone` is omitted. Must use the same form as `to`. A numeric UTC offset (for example `+05:45`) is rejected when `timezone` is set; pass a calendar day or a `Z` instant instead. Defaults to 30 days before `to` for day windows, or 168 hours before `to` for hour windows. ' schema: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ example: '2026-05-01' - name: to in: query required: false description: 'Inclusive end of the window, in the same form as `from`. Defaults to today, or the current hour for an hour window. A day window may not exceed 365 days and an hour window 720 hours. ' schema: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: compare in: query required: false description: 'Set to `previous_period` to include the received-message count for the immediately preceding window of equal length. The response also includes the change between the two, so you can show "+X% vs last period" without a second request. ' schema: $ref: '#/components/schemas/StatsComparePeriod' responses: '200': description: Total received messages for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSInboundStatsSummaryResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/inbound/daily: get: operationId: getSMSInboundStatsDaily summary: Get daily inbound SMS statistics description: 'Returns the number of messages your numbers received, one row per calendar day. Rows use the time the carrier received each message, and days with no messages contain a zero count. Each row contains only a count because a received message has one state. Use the outbound statistics endpoints for lifecycle and delivery-latency data about messages you send. The maximum window is 365 days; a longer range returns 422. Set `timezone` to bucket rows by your local calendar day instead of UTC.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.inbound.daily security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' responses: '200': description: Received-message counts per day for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSInboundStatsResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/inbound/hourly: get: operationId: getSMSInboundStatsHourly summary: Get hourly inbound SMS statistics description: 'Returns the number of messages your numbers received, one row per hour. Rows use the time the carrier received each message, and hours with no messages contain a zero count. Each row contains only a count because a received message has one state. Use the outbound statistics endpoints for lifecycle and delivery-latency data about messages you send. The maximum window is 720 hours; a longer range returns 422. Set `timezone` to bucket rows by your local hour instead of UTC.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.inbound.hourly security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start of the window (inclusive), an RFC 3339 instant truncated to the hour. Defaults to 7 days (168 hours) before `to` when omitted. schema: type: string format: date-time example: '2026-05-01T00:00:00Z' - name: to in: query required: false description: End of the window (inclusive), an RFC 3339 instant truncated to the hour. Defaults to the current hour when omitted. Window may not exceed 720 hours. A numeric UTC offset (for example `+05:45`) is rejected when `timezone` is set; pass a calendar day or a `Z` instant instead. schema: type: string format: date-time example: '2026-05-25T23:00:00Z' - $ref: '#/components/parameters/StatsTimezone' responses: '200': description: Received-message counts per hour for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSInboundStatsResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/inbound/countries: get: operationId: getSMSInboundStatsByCountry summary: Get inbound SMS statistics by country description: 'Returns the number of messages your numbers received, grouped by the receiving number''s country. Rows are ranked by volume, highest first, and use the time the carrier received each message. Each row contains only a count because a received message has one state. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to resolve the period against your local calendar.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.inbound.by_country security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: limit in: query required: false description: Maximum rows to return, ranked by volume. Defaults to 50; the maximum is 200, and asking for more returns 422 rather than silently returning fewer. schema: type: integer minimum: 1 maximum: 200 default: 50 example: 50 responses: '200': description: Received-message volume by country for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSInboundStatsByCountryResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/inbound/operators: get: operationId: getSMSInboundStatsByOperator summary: Get inbound SMS statistics by operator description: 'Returns the number of messages your numbers received, grouped by the sender''s mobile operator. Rows are ranked by volume, highest first, and use the time the carrier received each message. Operators are identified by MCC-MNC when the carrier reports it. Each row contains only a count because a received message has one state. Messages without a reported sending operator are excluded, so the rows can sum to less than the summary total. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to resolve the period against your local calendar.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.inbound.by_operator security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: limit in: query required: false description: Maximum rows to return, ranked by volume. Defaults to 50; the maximum is 200, and asking for more returns 422 rather than silently returning fewer. schema: type: integer minimum: 1 maximum: 200 default: 50 example: 50 responses: '200': description: Received-message volume by operator for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSInboundStatsByOperatorResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk /v1/sms/stats/inbound/numbers: get: operationId: getSMSInboundStatsByNumber summary: Get inbound SMS statistics by number description: 'Returns how many messages each of your numbers received. Rows are ranked by volume, highest first, and use the time the carrier received each message. Each row contains only a count because a received message has one state. The maximum window is 365 days; a longer range returns `422`. Set `timezone` to resolve the period against your local calendar.' tags: - sms-stats x-audiences: - public - command x-snippet-key: sms.stats.inbound.by_number security: - BearerAuth: [] - CookieAuth: [] parameters: - name: from in: query required: false description: Start date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to 30 days before `to` when omitted. schema: type: string format: date example: '2026-05-01' - name: to in: query required: false description: End date (inclusive), YYYY-MM-DD. Interpreted as a calendar day in `timezone` (a UTC day when `timezone` is omitted). Defaults to today in that timezone when omitted. Window may not exceed 365 days. schema: type: string format: date example: '2026-05-25' - $ref: '#/components/parameters/StatsTimezone' - name: limit in: query required: false description: Maximum rows to return, ranked by volume. Defaults to 50; the maximum is 200, and asking for more returns 422 rather than silently returning fewer. schema: type: integer minimum: 1 maximum: 200 default: 50 example: 50 responses: '200': description: Received-message volume by number for the requested period. content: application/json: schema: $ref: '#/components/schemas/SMSInboundStatsByNumberResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/Unprocessable' '429': $ref: '#/components/responses/RateLimited' '500': $ref: '#/components/responses/InternalError' '503': $ref: '#/components/responses/ServiceUnavailable' x-surfaces: - cli - make - mcp - n8n - sdk components: schemas: SMSStatsSeriesPeriod: type: object additionalProperties: false description: 'The window and bucket grain the response covers, echoed from the request, plus the freshness boundary the data is current to. ' required: - from - to - grain properties: from: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ readOnly: true 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. example: '2026-05-01' to: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ readOnly: true 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. example: '2026-05-31' grain: $ref: '#/components/schemas/StatsGrain' readOnly: true data_as_of: type: - string - 'null' format: date-time readOnly: true description: 'Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable. ' example: '2026-05-25T14:03:10Z' SMSStatsByStatusResponse: type: object additionalProperties: false description: Lifecycle-status breakdown for the requested period, ordered by message count descending. Statuses with no activity are omitted. required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to. data: type: array readOnly: true description: Status breakdown rows, one per lifecycle status with activity, ordered by count descending. Empty when no messages had activity in the period. items: $ref: '#/components/schemas/SMSStatusStatsPoint' total: type: integer minimum: 0 readOnly: true description: Number of distinct lifecycle statuses with activity in the period (at most seven). Equal to the number of rows returned, since this breakdown is never capped. example: 5 ErrorBody: type: object additionalProperties: false required: - type - code - name - message - doc_url - request_id properties: type: type: string minLength: 1 description: Broad category for coarse client branching. enum: - auth_error - bad_request_error - billing_error - conflict_error - gone_error - internal_error - misdirected_error - not_found_error - not_implemented_error - payload_too_large_error - permission_error - precondition_error - rate_limit_error - service_unavailable_error - too_early_error - validation_error code: type: string minLength: 1 pattern: ^E\d{5}$ description: Opaque, stable, unique error identifier. Never reused. name: type: string minLength: 1 description: Human-readable slug for log readability. Paired with code, never replaces it. message: type: string minLength: 1 description: Human-readable description. Not stable; clients must not parse it. param: type: string minLength: 1 description: Identifies the offending field. Omitted when not applicable. doc_url: type: string minLength: 1 format: uri description: Stable link to the docs page for this error code. request_id: type: string minLength: 1 description: Request correlation ID for support and troubleshooting. Also returned in the `X-Request-Id` response header. vendor_code: type: string minLength: 1 description: 'Verbatim error code from an external system, such as an SMTP response code or a payment decline code. Present only when the code may help you resolve the error. ' details: type: array description: Per-field validation errors. Present only on validation_error responses. items: $ref: '#/components/schemas/ErrorDetail' remediation: type: string minLength: 1 description: A human-readable next step to resolve this error. Present when a recovery is known. next: type: array description: 'The steps that resolve this error. Perform them in order, re-reading after each; a `wait` or `terminal` step is always last. Present for errors with a well-defined recovery, such as unmet preconditions and conflicts. ' items: $ref: '#/components/schemas/NextAction' StatsComparePeriod: type: string description: 'Set to `previous_period` to also return the same figures for the immediately preceding window of equal length, plus the change between the two, so you can show "+X% vs last period" without a second request. ' enum: - previous_period SMSStatsByTagResponse: type: object additionalProperties: false description: Per-tag breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200). required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to. data: type: array readOnly: true description: Tag breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no tagged messages were sent in the period. items: $ref: '#/components/schemas/SMSTagStatsPoint' total: type: integer minimum: 0 readOnly: true description: 'Total number of distinct tags with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. ' example: 18 SMSStatsSummary: type: object additionalProperties: false description: 'Single-row aggregate across the full requested period, covering SMS lifecycle counts plus the derived delivery and failure rates, and latency percentiles. Use this endpoint for KPI tiles and reporting; the daily and hourly endpoints carry the same counts per bucket. Every count is a sum of per-bucket counts across the window. Latency percentiles are computed across the whole period rather than summed per bucket. Rates are null when their denominator is zero. ' required: - period - delivery - latency properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The window the response covers (echoed back from the request, day or hour grain), plus `data_as_of`, the freshness boundary the data is current to. delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' comparison: readOnly: true allOf: - $ref: '#/components/schemas/SMSStatsComparison' SMSInboundNumberStatsPoint: type: object additionalProperties: false readOnly: true description: 'Received-message volume for one of your numbers. ' required: - number - received properties: number: type: string minLength: 1 readOnly: true description: The Bird number the messages arrived on, in E.164, or the short code they were sent to. This is the same value the message resource exposes as `to`. example: '+14155557701' received: type: integer minimum: 0 readOnly: true description: Distinct messages received on this number during the period. example: 412 SMSErrorCodeStatsPoint: type: object additionalProperties: false description: Delivery and latency statistics for one standardized failure reason over the requested period. required: - error_code - delivery - latency properties: error_code: readOnly: true description: Standardized failure reason this row aggregates. Matches the `error_code` message-list filter. allOf: - $ref: '#/components/schemas/SMSErrorCode' delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' trend: type: array readOnly: true description: Per-bucket lifecycle counts for this error code, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`. items: $ref: '#/components/schemas/SMSStatsPoint' SMSStatsComparison: type: object additionalProperties: false readOnly: true description: 'The same statistics for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request. ' required: - period - delivery - latency - delta properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: Equal-length window ending immediately before the requested start. delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' delta: readOnly: true allOf: - $ref: '#/components/schemas/SMSStatsComparisonDelta' SMSDeliveryStats: type: object additionalProperties: false readOnly: true description: 'SMS lifecycle counts and rates for a whole period or breakdown. Counts use the message send time, so a later delivery stays attributed to the bucket in which the message was sent. Rates are null when their denominator is zero. ' required: - accepted - sent - delivered - undelivered - failed - rejected - expired - delivery_rate - failure_rate properties: accepted: type: integer minimum: 0 readOnly: true description: Distinct messages accepted for sending after admission checks. This is the denominator for `delivery_rate` and `failure_rate`. example: 14820 sent: type: integer minimum: 0 readOnly: true description: Distinct messages handed off to the carrier for delivery. example: 14810 delivered: type: integer minimum: 0 readOnly: true description: Distinct messages the carrier confirmed as delivered to the handset. example: 14720 undelivered: type: integer minimum: 0 readOnly: true description: Distinct messages the carrier reported as not delivered. example: 60 failed: type: integer minimum: 0 readOnly: true description: Distinct messages that failed during sending. example: 25 rejected: type: integer minimum: 0 readOnly: true description: Distinct messages rejected before any send attempt, for example by sending policy or a message-generation failure. example: 10 expired: type: integer minimum: 0 readOnly: true description: Distinct messages that could not be delivered within their validity window and expired. example: 5 delivery_rate: type: - number - 'null' minimum: 0 maximum: 1 readOnly: true description: 'Share of accepted messages that were delivered, computed as `delivered / accepted`. Null when no messages were accepted in scope. ' example: 0.9932 failure_rate: type: - number - 'null' minimum: 0 readOnly: true description: 'Share of accepted messages that ultimately failed, computed as `(undelivered + failed + expired) / accepted`. Null when no messages were accepted in scope. ' example: 0.0061 SMSDeliveryCounts: type: object additionalProperties: false readOnly: true description: 'SMS lifecycle counts for a time bucket. Counts use the message send time, so a message accepted on Monday and delivered on Tuesday counts in Monday''s bucket. Rates are available only for whole periods and breakdowns. ' required: - accepted - sent - delivered - undelivered - failed - rejected - expired properties: accepted: type: integer minimum: 0 readOnly: true description: Distinct messages accepted for sending after admission checks. example: 14820 sent: type: integer minimum: 0 readOnly: true description: Distinct messages handed off to the carrier for delivery. example: 14810 delivered: type: integer minimum: 0 readOnly: true description: Distinct messages the carrier confirmed as delivered to the handset. example: 14720 undelivered: type: integer minimum: 0 readOnly: true description: Distinct messages the carrier reported as not delivered. example: 60 failed: type: integer minimum: 0 readOnly: true description: Distinct messages that failed during sending. example: 25 rejected: type: integer minimum: 0 readOnly: true description: Distinct messages rejected before any send attempt, for example by sending policy or a message-generation failure. example: 10 expired: type: integer minimum: 0 readOnly: true description: Distinct messages that could not be delivered within their validity window and expired. example: 5 SMSStatsByCategoryResponse: type: object additionalProperties: false description: Per-category breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200). required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to. data: type: array readOnly: true description: Category breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period. items: $ref: '#/components/schemas/SMSCategoryStatsPoint' total: type: integer minimum: 0 readOnly: true description: 'Total number of distinct categories with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. ' example: 8 SMSStatsByErrorCodeResponse: type: object additionalProperties: false description: Per-error-code breakdown for the requested period, ranked by the `sort` metric (default `failed`) descending and capped at the requested `limit` (default 50, max 200). required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to. data: type: array readOnly: true description: Error-code breakdown rows, ranked by the `sort` metric (default `failed`) descending. Empty when no delivery failures occurred in the period. items: $ref: '#/components/schemas/SMSErrorCodeStatsPoint' total: type: integer minimum: 0 readOnly: true description: 'Total number of distinct error codes with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. ' example: 17 SMSInboundStatsComparison: type: object additionalProperties: false readOnly: true description: 'The received-message count for the equal-length, inclusive period ending immediately before the requested start, together with the change between the two periods. Present only when `compare=previous_period` is requested. The change is already computed, so a percentage difference needs no second request. ' required: - period - received - delta properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: Equal-length window ending immediately before the requested start. received: type: integer minimum: 0 readOnly: true description: Distinct messages received in the preceding period. example: 3980 delta: readOnly: true allOf: - $ref: '#/components/schemas/SMSInboundStatsComparisonDelta' SMSLatencyQuantiles: type: object additionalProperties: false readOnly: true description: 'Approximate p50, p95, and p99 latency percentiles in milliseconds for one latency family. All three are null when no qualifying event contributed a measurement. ' required: - p50_ms - p95_ms - p99_ms properties: p50_ms: type: - integer - 'null' minimum: 0 readOnly: true description: Median (50th percentile) latency in milliseconds. Null when no qualifying event contributed a measurement. example: 420 p95_ms: type: - integer - 'null' minimum: 0 readOnly: true description: 95th percentile latency in milliseconds. Null when no qualifying event contributed a measurement. example: 1820 p99_ms: type: - integer - 'null' minimum: 0 readOnly: true description: 99th percentile latency in milliseconds. Null when no qualifying event contributed a measurement. example: 4920 SMSStatsComparisonDelta: type: object additionalProperties: false readOnly: true description: 'Changes from the previous period. A `*_pct_change` value is the signed relative change `(current - previous) / previous` and is null when the previous count is zero. A `*_rate_pp` value is the signed difference between rate fractions and is null when either rate is undefined. ' required: - accepted_pct_change - sent_pct_change - delivered_pct_change - undelivered_pct_change - failed_pct_change - rejected_pct_change - expired_pct_change - delivery_rate_pp - failure_rate_pp properties: accepted_pct_change: type: - number - 'null' readOnly: true description: Relative change in accepted messages (`delivery.accepted`) versus the previous period, as a signed fraction. Null when the previous period accepted none. example: 0.508 sent_pct_change: type: - number - 'null' readOnly: true description: Relative change in sent messages (`delivery.sent`) versus the previous period, as a signed fraction. Null when the previous period had none. example: 0.121 delivered_pct_change: type: - number - 'null' readOnly: true description: Relative change in delivered messages (`delivery.delivered`) versus the previous period, as a signed fraction. Null when the previous period delivered none. example: 0.122 undelivered_pct_change: type: - number - 'null' readOnly: true description: Relative change in undelivered messages (`delivery.undelivered`) versus the previous period, as a signed fraction. Null when the previous period had none. example: -0.031 failed_pct_change: type: - number - 'null' readOnly: true description: Relative change in failed messages (`delivery.failed`) versus the previous period, as a signed fraction. Null when the previous period had none. example: -0.018 rejected_pct_change: type: - number - 'null' readOnly: true description: Relative change in rejected messages (`delivery.rejected`) versus the previous period, as a signed fraction. Null when the previous period had none. example: 0 expired_pct_change: type: - number - 'null' readOnly: true description: Relative change in expired messages (`delivery.expired`) versus the previous period, as a signed fraction. Null when the previous period had none. example: 0.04 delivery_rate_pp: type: - number - 'null' minimum: -1 maximum: 1 readOnly: true description: Signed difference between this period's and the previous period's delivery rate, both fractions in [0,1] (multiply by 100 for percentage points). Null when either period's delivery rate is undefined. example: 0.004 failure_rate_pp: type: - number - 'null' readOnly: true description: 'Signed difference between the current and previous failure-rate fractions. Multiply by 100 for percentage points. The value can fall outside `[-1, 1]` because a message can contribute to more than one failure outcome and high-volume counts are approximate. Null when either rate is undefined. ' example: -0.0008 SMSTagStatsPoint: type: object additionalProperties: false description: Aggregate delivery and latency stats for a single tag (`name:value`) over the requested period. required: - tag - delivery - latency properties: tag: type: string minLength: 1 readOnly: true description: 'The tag this row aggregates, in `name:value` form. Each distinct name-and-value pair is its own row, and a message carrying several tags is counted once under each of them, so rows do not sum to the period total. ' example: campaign:summer_sale delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' trend: type: array readOnly: true description: 'Per-bucket lifecycle-count series for this tag over the window, bucketed by `trend_grain`. Sparse, so only buckets with activity are present rather than zero-filled, unlike the daily and hourly series. Present only when `include_trend=true`. ' items: $ref: '#/components/schemas/SMSStatsPoint' SMSStatsByOriginatorResponse: type: object additionalProperties: false description: Per-originator breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200). required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to. data: type: array readOnly: true description: Originator breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period. items: $ref: '#/components/schemas/SMSOriginatorStatsPoint' total: type: integer minimum: 0 readOnly: true description: 'Total number of distinct originators with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. ' example: 42 SMSInboundCountryStatsPoint: type: object additionalProperties: false readOnly: true description: 'Received-message volume for one country. ' required: - country - received properties: country: type: string minLength: 1 readOnly: true description: The country of the Bird number the messages arrived on, as an ISO 3166-1 alpha-2 code. This identifies where the message was received. It does not identify the sender's country. example: US received: type: integer minimum: 0 readOnly: true description: Distinct messages received on numbers in this country during the period. example: 1840 SMSInboundStatsByOperatorResponse: type: object additionalProperties: false description: 'Received-message volume broken down by operator, ranked by volume. ' required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' data: type: array readOnly: true description: 'One row per operator with activity in the period, most messages first, capped at the requested `limit`. An operator with no messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis. ' items: $ref: '#/components/schemas/SMSInboundOperatorStatsPoint' total: type: integer minimum: 0 readOnly: true description: Total number of distinct sending operators with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. example: 38 SMSStatsByCountryResponse: type: object additionalProperties: false description: Per-country breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200). required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to. data: type: array readOnly: true description: Country breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period. items: $ref: '#/components/schemas/SMSCountryStatsPoint' total: type: integer minimum: 0 readOnly: true description: 'Total number of distinct destination countries with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. ' example: 42 SMSStatsSummaryPeriod: type: object additionalProperties: false description: 'The window the server actually computed against. The summary serves two window grains: calendar days (bounds are YYYY-MM-DD) and hours (bounds are RFC 3339 instants on the hour). The grain of `from` and `to` mirrors the grain of the request''s bounds. ' required: - from - to properties: from: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ readOnly: true description: Inclusive start of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary. example: '2026-05-01' to: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ readOnly: true description: Inclusive end of the window, as a calendar day (`YYYY-MM-DD`) or an RFC 3339 hour boundary. example: '2026-05-25' data_as_of: type: - string - 'null' format: date-time readOnly: true description: 'Latest time reflected in the statistics. More recent events might not be included yet. Null when the freshness boundary is unavailable. ' example: '2026-05-25T14:03:10Z' SMSErrorCode: type: string minLength: 1 x-extensible-enum: - invalid_destination - unreachable - blocked_by_carrier - blocked_by_recipient - landline_unreachable - content_rejected - sender_unregistered - recipient_opted_out - provider_unavailable - insufficient_balance - unknown description: 'Standardized failure reason: - `invalid_destination`: The number is unassigned, ported out, or malformed. - `unreachable`: The handset is off or outside coverage. - `blocked_by_carrier`: The carrier filtered the message. - `blocked_by_recipient`: The recipient device blocked the sender. - `landline_unreachable`: The destination is a landline that does not accept SMS. - `content_rejected`: The carrier rejected the content. - `sender_unregistered`: The sender is not registered for the destination. - `recipient_opted_out`: The recipient is on a suppression list. - `provider_unavailable`: The provider remained unavailable after retries. - `insufficient_balance`: The workspace wallet could not fund the send. - `unknown`: The failure could not be classified. This is an open enum. Accept unrecognized values. ' SMSInboundStatsResponse: type: object additionalProperties: false description: 'Received-message time series. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order. ' required: - period - data properties: period: $ref: '#/components/schemas/SMSStatsSeriesPeriod' data: type: array readOnly: true description: One row per bucket (day or hour, per the grain) in the period, in chronological order. Buckets with no activity are included with a count of zero, so the series charts continuously without client-side gap handling. items: $ref: '#/components/schemas/SMSInboundStatsPoint' SMSStatsLifecycleSortMetric: type: string default: failed description: 'Metric to rank breakdown rows by, applied descending. Shared by the breakdowns whose rows carry lifecycle counts only, with no derived rates to sort on. ' enum: - accepted - sent - delivered - undelivered - failed - rejected - expired SMSInboundStatsByCountryResponse: type: object additionalProperties: false description: 'Received-message volume broken down by country, ranked by volume. ' required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' data: type: array readOnly: true description: 'One row per country with activity in the period, most messages first, capped at the requested `limit`. A country with no messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis. ' items: $ref: '#/components/schemas/SMSInboundCountryStatsPoint' total: type: integer minimum: 0 readOnly: true description: Total number of distinct countries the messages arrived in with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. example: 12 SMSInboundStatsByNumberResponse: type: object additionalProperties: false description: 'Received-message volume broken down by number, ranked by volume. ' required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' data: type: array readOnly: true description: 'One row per number with activity in the period, most messages first, capped at the requested `limit`. A number with no messages in the period is absent rather than zero-filled, because unlike a time bucket it is not part of a continuous axis. ' items: $ref: '#/components/schemas/SMSInboundNumberStatsPoint' total: type: integer minimum: 0 readOnly: true description: Total number of distinct numbers with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. example: 6 ErrorDetail: type: object additionalProperties: false required: - param - message properties: param: type: string minLength: 1 description: 'Dotted field path, such as `to[0].email`, `subject`, or `.`. When the request was rejected for a query parameter the endpoint does not declare, this carries that parameter''s name instead of a field path. ' message: type: string minLength: 1 description: What is wrong with this field. SMSLatencyStats: type: object additionalProperties: false readOnly: true description: 'Latency percentiles in milliseconds for the requested scope: - `processing`: From acceptance to carrier handoff. - `delivery`: From carrier handoff to delivery confirmation. - `total`: From acceptance to delivery confirmation. Each family is omitted when no qualifying event contributes a measurement. Individual percentiles can also be null. ' properties: processing: $ref: '#/components/schemas/SMSLatencyQuantiles' delivery: $ref: '#/components/schemas/SMSLatencyQuantiles' total: $ref: '#/components/schemas/SMSLatencyQuantiles' StatsTrendGrain: type: string enum: - daily - hourly default: daily description: Bucket grain for a stats trend series. NextAction: type: object additionalProperties: false required: - kind - description properties: kind: type: string minLength: 1 x-extensible-enum: - operation - external - wait - terminal description: "What you do about this step.\n\n- `operation`: call the operation named in `operation`, then\n read again.\n- `external`: act somewhere this API does not reach, then read\n again.\n- `wait`: nothing is asked of you, so read again later.\n- `terminal`: nothing you do resolves this, so stop retrying.\n\nTolerate a value you do not recognize: show the `description` and\noffer no action.\n" description: type: string minLength: 1 description: A short, human-readable label for the step, suitable for display. operation: type: string minLength: 1 description: 'The operationId to call. Present only when `kind` is `operation`. The operation''s own schema says how to call it; this says only which one, and what to address it with. ' params: type: object additionalProperties: type: string minLength: 1 description: 'The parameters that address the operation, by name: `{"sender_id": "…"}` for an operation on `/v1/sms/senders/{sender_id}/requirements`. A parameter the operation takes in its query string is given the same way, so an operation addressed as `?subject_id=` carries `{"subject_id": "…"}`. Every parameter the call needs is here, whether its value came from the thing you were acting on or is fixed for this step, so you can make the call from this object alone. Present only when `kind` is `operation` and the operation names a subject. A request body, when the operation takes one, is described by the operation''s own schema and never appears here. ' url: type: string format: uri description: 'A URL to open. Present only when `kind` is `external`, and only when the step has one. An external step whose `description` says to go and do something with no URL to open is normal. ' SMSStatsSortMetric: type: string default: accepted description: 'Metric to rank breakdown rows by, applied descending. Shared by the volume breakdowns whose rows carry the full delivery and latency block (originators, countries, categories, carriers). Any lifecycle count or derived rate may be used; rows whose rate is undefined (zero denominator) sort last. ' enum: - accepted - sent - delivered - undelivered - failed - rejected - expired - delivery_rate - failure_rate SMSCategoryStatsPoint: type: object additionalProperties: false description: Aggregate delivery and latency stats for a single message category over the requested period. required: - category - delivery - latency properties: category: type: string minLength: 1 readOnly: true description: The category this row aggregates, as set at send time. `transactional` is one-to-one messaging triggered by a user action; `marketing` is bulk sending. New categories may be added over time. example: transactional delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' trend: type: array readOnly: true description: Per-bucket lifecycle counts for this category, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`. items: $ref: '#/components/schemas/SMSStatsPoint' SMSStatusStatsPoint: type: object additionalProperties: false description: The number of messages that ended the requested period in a single lifecycle status. This transposes the lifecycle counts into one row per status, so it carries no rates or latency. required: - status - count properties: status: type: string minLength: 1 readOnly: true description: 'The lifecycle status this row counts. These are successive lifecycle stages. The `accepted` status was admitted for sending, `sent` was handed to the carrier, and `delivered` was confirmed by the carrier. The `undelivered`, `failed`, and `expired` statuses are failure outcomes. The `rejected` status was refused before a send attempt. Counted outcomes are a subset of the full message status vocabulary. The pre-send `scheduled`, cancellation `canceled`, and inbound-only `received` statuses are not send outcomes, so they never appear here. ' enum: - accepted - sent - delivered - undelivered - failed - rejected - expired example: delivered count: type: integer minimum: 0 readOnly: true description: Distinct messages that reached this lifecycle status in the period, attributed to the message's send time rather than the event's own. example: 14720 SMSStatsPoint: type: object additionalProperties: false readOnly: true description: 'SMS lifecycle counts for one time bucket (a calendar day or hour), bucketed by send time. Every count in a bucket describes the messages accepted in it, regardless of when their later events arrived. Per-bucket values include counts only. The summary and breakdown endpoints report rates and latency as whole-window aggregates. ' required: - bucket - delivery properties: bucket: type: string minLength: 1 readOnly: true description: The day (YYYY-MM-DD) or hour (RFC 3339, on the hour) this point covers, matching the period's grain. example: '2026-05-25' delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryCounts' Error: type: object additionalProperties: false required: - error properties: error: $ref: '#/components/schemas/ErrorBody' SMSCountryStatsPoint: type: object additionalProperties: false description: Aggregate delivery and latency stats for a single destination country over the requested period. required: - country - delivery - latency properties: country: type: string minLength: 1 readOnly: true description: The destination country this row aggregates, as an ISO 3166-1 alpha-2 code. example: US delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' trend: type: array readOnly: true description: Per-bucket lifecycle counts for this country, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`. items: $ref: '#/components/schemas/SMSStatsPoint' SMSStatsResponse: type: object additionalProperties: false description: 'Time-series stats payload. `period` echoes the range and bucket grain the server computed against; `data` is one row per bucket in chronological order. ' required: - period - data properties: period: $ref: '#/components/schemas/SMSStatsSeriesPeriod' data: type: array readOnly: true description: One row per day or hour in chronological order. Buckets with no activity contain zero counts. items: $ref: '#/components/schemas/SMSStatsPoint' SMSInboundStatsSummaryResponse: type: object additionalProperties: false description: 'Total received messages over the requested period. ' required: - period - received properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' received: type: integer minimum: 0 readOnly: true description: Distinct messages received in the period, counted by the time the carrier received them. example: 4210 comparison: readOnly: true allOf: - $ref: '#/components/schemas/SMSInboundStatsComparison' SMSInboundStatsPoint: type: object additionalProperties: false readOnly: true description: One time bucket of received-message counts, attributed by arrival time. required: - bucket - received properties: bucket: type: string minLength: 1 pattern: ^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2}))?$ readOnly: true description: Start of the bucket this row covers, as a calendar day (YYYY-MM-DD) for the daily series or an hour boundary (RFC 3339) for the hourly one. example: '2026-05-01' received: type: integer minimum: 0 readOnly: true description: Distinct messages received in this bucket, counted by the time the carrier received them. example: 128 SMSCarrierStatsPoint: type: object additionalProperties: false description: Aggregate delivery and latency stats for a single delivery carrier over the requested period. required: - carrier - delivery - latency properties: carrier: type: string minLength: 1 readOnly: true description: The delivery carrier this row aggregates, as resolved for the destination handset. example: Verizon delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' trend: type: array readOnly: true description: Per-bucket lifecycle counts for this carrier, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`. items: $ref: '#/components/schemas/SMSStatsPoint' SMSStatsByCarrierResponse: type: object additionalProperties: false description: Per-carrier breakdown for the requested period, ranked by the `sort` metric (default `accepted`) descending and capped at the requested `limit` (default 50, max 200). required: - period - data - total properties: period: $ref: '#/components/schemas/SMSStatsSummaryPeriod' description: The date range the response covers (echoed back from the request), plus `data_as_of`, the freshness boundary the data is current to. data: type: array readOnly: true description: Carrier breakdown rows, ranked by the `sort` metric (default `accepted`) descending. Empty when no messages were sent in the period. items: $ref: '#/components/schemas/SMSCarrierStatsPoint' total: type: integer minimum: 0 readOnly: true description: 'Total number of distinct carriers with activity in the period, regardless of `limit`. When it exceeds the number of rows returned, the ranking was capped; raise `limit` (up to 200) or narrow the window to see more. ' example: 24 SMSInboundOperatorStatsPoint: type: object additionalProperties: false readOnly: true description: 'Received-message volume for one mobile operator. ' required: - mcc_mnc - received properties: mcc_mnc: type: string minLength: 1 readOnly: true description: Mobile country code and mobile network code of the network the sending subscriber is on. The breakdown keys on this rather than on an operator name because the carrier reports a name only where a surcharge applies, which would leave most of the world in one unnamed bucket. example: '311480' received: type: integer minimum: 0 readOnly: true description: Distinct messages received from senders on this operator during the period. example: 640 SMSInboundStatsComparisonDelta: type: object additionalProperties: false readOnly: true description: 'The change from the preceding period to the requested one. The `received_pct_change` field is a signed relative change, computed as `(current - previous) / previous`. A value of `0.5` means 50% higher, and `-0.2` means 20% lower. The field is null when the previous period received none. ' required: - received_pct_change properties: received_pct_change: type: - number - 'null' readOnly: true description: Relative change in received messages versus the previous period, as a signed fraction. Null when the previous period received none. example: 0.058 StatsGrain: type: string minLength: 1 enum: - day - hour readOnly: true description: The bucket grain of the series, either `day` or `hour`. example: day SMSOriginatorStatsPoint: type: object additionalProperties: false description: Aggregate delivery and latency stats for a single originator (the sender address messages were sent from) over the requested period. required: - originator - delivery - latency properties: originator: type: string minLength: 1 readOnly: true description: Sender address this row aggregates, either an alphanumeric sender ID or a phone number. Matches the message `from` value. example: BirdSMS delivery: readOnly: true allOf: - $ref: '#/components/schemas/SMSDeliveryStats' latency: readOnly: true allOf: - $ref: '#/components/schemas/SMSLatencyStats' trend: type: array readOnly: true description: Per-bucket lifecycle counts for this originator, using `trend_grain`. Includes only buckets with activity. Present when `include_trend=true`. items: $ref: '#/components/schemas/SMSStatsPoint' parameters: StatsTimezone: name: timezone in: query required: false description: 'IANA timezone identifier used to group statistics, for example `Asia/Kathmandu`. The default is UTC. Day and hour boundaries, including the default window when `from` and `to` are omitted, follow this timezone. When this parameter is set, pass `from` and `to` as calendar days or `Z` instants instead of timestamps with explicit UTC offsets. ' schema: type: string minLength: 1 example: Asia/Kathmandu responses: InternalError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' RateLimited: description: Rate limit exceeded headers: RateLimit: $ref: '#/components/headers/RateLimit' RateLimit-Policy: $ref: '#/components/headers/RateLimit-Policy' Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' Forbidden: description: Insufficient permissions content: application/json: schema: $ref: '#/components/schemas/Error' ServiceUnavailable: description: 'The service is temporarily unavailable. If `Retry-After` is present, wait for that delay before retrying; otherwise, retry with exponential backoff. Reuse the same idempotency key and request when retrying a mutation. ' headers: Retry-After: $ref: '#/components/headers/RetryAfter' content: application/json: schema: $ref: '#/components/schemas/Error' Unprocessable: description: 'The request has invalid field values, violates a business rule, or carries a query parameter the endpoint does not declare. Field validation errors use `type: validation_error` and include the affected fields in `details`. Business-rule errors identify the failed rule in `type`. ' content: application/json: schema: $ref: '#/components/schemas/Error' BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Authentication required content: application/json: schema: $ref: '#/components/schemas/Error' headers: RetryAfter: description: 'Number of seconds to wait before retrying the request. ' schema: type: integer minimum: 0 example: 35 RateLimit: description: 'Remaining capacity for the request''s rate-limit policy as an IETF Structured Field. Format: `"";r=;t=`. ' schema: type: string example: '"email_send";r=842;t=35' RateLimit-Policy: description: 'Effective quota for the request''s rate-limit policy as an IETF Structured Field. Format: `"";q=;w=`. ' schema: type: string example: '"email_send";q=1000;w=60' securitySchemes: BearerAuth: type: http scheme: bearer description: 'Pass the API key as a bearer token in the `Authorization` header. Keys use the format `bk_{region}_*`. The prefix identifies the region and selects the API endpoint. Official Bird SDKs and the CLI derive the region from the key. ' CookieAuth: type: apiKey in: cookie name: bird_session description: 'Session cookie set after signing in to the Bird dashboard. The cookie value is an opaque session token; no session data is stored in the cookie itself. ' RealtimeKey: type: apiKey in: header name: X-Realtime-Key description: 'The Realtime app key. Together with `X-Realtime-Secret`, it authenticates a request to the Realtime API in addition to the workspace credential. Both values come from the app''s credentials and must belong to the calling workspace. Official Bird SDKs accept the pair as client configuration. ' RealtimeSecret: type: apiKey in: header name: X-Realtime-Secret description: 'The Realtime app secret paired with `X-Realtime-Key`. The API returns the secret only when the key is created and does not store it. Create a new key and revoke the current key if you lose the secret. Official Bird SDKs accept the pair as client configuration. '