@messagebird/sdk 0.9.3 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1185,7 +1185,8 @@ const client = createClient(createConfig());
1185
1185
  /**
1186
1186
  * List messages
1187
1187
  *
1188
- * Returns a paginated list of email messages in the workspace, newest first.
1188
+ * Returns the workspace's sent and scheduled messages, newest first, as a cursor page. Each item carries the aggregate delivery `status` and per-state recipient counts, not the message body. Combine filters to narrow the page: `status`, `category`, `tag`, exact `to`/`from` address, and a `created_after`/`created_before` time window.
1189
+ *
1189
1190
  */
1190
1191
  const listEmailMessages = (options) => (options?.client ?? client).get({
1191
1192
  security: [{
@@ -1202,9 +1203,22 @@ const listEmailMessages = (options) => (options?.client ?? client).get({
1202
1203
  /**
1203
1204
  * Send a message
1204
1205
  *
1205
- * Sends an email to the recipients you list explicitly in `to`/`cc`/`bcc`. Use this for transactional sends (receipts, password resets, alerts) and for marketing sends where you have the recipient addresses on hand. For sends targeting a stored audience by reference, use POST /v1/email/broadcasts. The `category` field controls suppression policy independently — set it to `marketing` when sending marketing content from this endpoint. The 202 response is returned only after the message is safely accepted for delivery. If the sender domain is not verified, or all recipients are suppressed, the request fails immediately with a 422 — it is never accepted and then silently dropped. Other field-level validation failures also return 422.
1206
- * Recipient addresses on reserved testing domains are rejected with a 422 RecipientDomainNotAllowed error. This covers @example.com, @example.net, @example.org, @example.edu, @test.com, and any address under the reserved .test, .example, .invalid, or .localhost top-level domains. These placeholder domains cannot receive mail, and the resulting bounces would hurt your sender reputation — use a real recipient address instead.
1207
- * During onboarding, you can send without verifying a domain: use any sender address on the shared onboarding domain (for example onboarding@messagebird.dev). Such sends skip the sender-domain verification check, can only go to verified members of your workspace (other recipients are rejected with a 422 OnboardingRecipientNotAllowed error), and are subject to a daily recipient limit per organization (429 OnboardingSendLimitExceeded once exhausted).
1206
+ * Sends an email to the recipients you list explicitly in `to`/`cc`/`bcc`. Use it for
1207
+ * transactional sends (receipts, password resets, alerts) and for marketing sends where
1208
+ * you have the recipient addresses on hand; to submit many independent messages in one
1209
+ * request, use [Send a batch of messages](/docs/api/reference/create-email-message-batch)
1210
+ * instead. The `category` field controls suppression policy independently of content:
1211
+ * set it to `marketing` when sending marketing content from this endpoint.
1212
+ *
1213
+ * The `202` response means the message is safely accepted for delivery, not yet
1214
+ * delivered. Fetch it by `id` or subscribe to webhook events to follow delivery. The
1215
+ * request never half-succeeds: an unverified sender domain or any field-level
1216
+ * validation failure rejects it immediately with a `422` naming the reason.
1217
+ * Suppression is evaluated per recipient after acceptance: suppressed recipients
1218
+ * surface as `rejected` on the message's recipient list, never as a synchronous
1219
+ * error. New workspaces can send from the shared onboarding domain before verifying
1220
+ * their own; the [quickstart](/docs/get-started/send-your-first-email) covers its
1221
+ * recipient and volume limits.
1208
1222
  *
1209
1223
  */
1210
1224
  const createEmailMessage = (options) => (options.client ?? client).post({
@@ -1226,7 +1240,7 @@ const createEmailMessage = (options) => (options.client ?? client).post({
1226
1240
  /**
1227
1241
  * Send a batch of messages
1228
1242
  *
1229
- * Accepts up to 100 independent email messages and queues them for delivery. All items are validated before any are queued if one fails validation, the entire batch is rejected. Field-level validation failures and business-rule failures (such as domain_not_verified or all_recipients_suppressed) both return 422. Attachments are allowed per message. Each message must stay within the 20 MB estimated generated message-size cap. The serialized JSON request body for the batch has a hard 20 MB cap.
1243
+ * Accepts up to 100 independent email messages and queues them for delivery. All items are validated before any are queued: if one fails validation, the entire batch is rejected. Field-level validation failures and business-rule failures (such as `domain_not_verified`) both return `422`. Suppression is evaluated per recipient after acceptance, never as a synchronous error. The `202` response returns one entry per message in submission order, each with its own `id` to fetch or correlate webhook events against. Attachments are allowed per message. Each message must stay within the 20 MB estimated generated message-size cap, and the serialized JSON request body for the whole batch has a hard 20 MB cap.
1230
1244
  *
1231
1245
  */
1232
1246
  const createEmailMessageBatch = (options) => (options.client ?? client).post({
@@ -1248,7 +1262,7 @@ const createEmailMessageBatch = (options) => (options.client ?? client).post({
1248
1262
  /**
1249
1263
  * Get a message
1250
1264
  *
1251
- * Returns a single email message object with aggregate delivery status and counts. Message body (html, text) is not returned it is not stored after delivery.
1265
+ * Returns a single message with its aggregate delivery `status` and per-state recipient counts. The response never includes the `html`/`text` bodies; when content storage is enabled for the send, fetch the stored bodies with [Get stored message content](/docs/api/reference/get-email-message-content). Per-recipient statuses and the event timeline are separate sub-resources.
1252
1266
  *
1253
1267
  */
1254
1268
  const getEmailMessage = (options) => (options.client ?? client).get({
@@ -1284,7 +1298,7 @@ const cancelEmailMessage = (options) => (options.client ?? client).post({
1284
1298
  /**
1285
1299
  * List contacts
1286
1300
  *
1287
- * Returns a paginated list of contacts in the workspace, newest first. Look up a single contact by its exact `email` or `external_id`, or search by email substring with `search`.
1301
+ * Returns a paginated list of contacts in the workspace, newest first. Look up a single contact by its exact `email` or `external_id`, or search by email substring with `q`.
1288
1302
  *
1289
1303
  */
1290
1304
  const listContacts = (options) => (options?.client ?? client).get({
@@ -1302,7 +1316,9 @@ const listContacts = (options) => (options?.client ?? client).get({
1302
1316
  /**
1303
1317
  * Create a contact
1304
1318
  *
1305
- * Creates a contact in the workspace. Contacts are unique by email address; creating a second contact with the same email returns a conflict error. The same applies to `external_id` a value already used by another contact returns a conflict error. Custom values in `data` must use property keys you created via the contact properties API, with values matching each property's declared type.
1319
+ * Creates a contact in the workspace. The email address is the contact's identity: it is stored trimmed and lowercased, and creating a second contact with the same email, or reusing another contact's `external_id`, returns a conflict error.
1320
+ *
1321
+ * To create or update many contacts in one request, or to write a contact without knowing whether the address already exists, use [Create or update contacts in bulk](/docs/api/reference/create-contact-batch) instead.
1306
1322
  *
1307
1323
  */
1308
1324
  const createContact = (options) => (options.client ?? client).post({
@@ -1324,7 +1340,9 @@ const createContact = (options) => (options.client ?? client).post({
1324
1340
  /**
1325
1341
  * Create or update contacts in bulk
1326
1342
  *
1327
- * Creates or updates up to 1,000 contacts in one request, matched by email address (trimmed and lowercased before matching). Existing contacts are updated with the supplied fields; new ones are created. Optionally adds every contact in the request to one or more audiences. Results are returned per contact, in submission order a failed entry does not abort the rest of the request.
1343
+ * Creates or updates up to 1,000 contacts in one request, matched by email address (trimmed and lowercased before matching). A new address creates a contact; an existing one is updated with the fields that entry supplies, and omitted fields keep their stored values. Optionally adds every contact in the request to up to 10 audiences.
1344
+ *
1345
+ * Each entry succeeds or fails on its own: the response lists one result per contact in submission order (`created`, `updated`, or `failed` with the reason), and a failed entry does not abort the rest. If the request itself is invalid, for example when an entry in `audience_ids` does not exist, the whole request fails with a validation error and no contacts are written.
1328
1346
  *
1329
1347
  */
1330
1348
  const createContactBatch = (options) => (options.client ?? client).post({
@@ -1346,7 +1364,7 @@ const createContactBatch = (options) => (options.client ?? client).post({
1346
1364
  /**
1347
1365
  * Delete a contact
1348
1366
  *
1349
- * Deletes a contact and removes it from every audience it belongs to. Suppression records for the address are not affected an unsubscribed or bounced address stays suppressed even after the contact is deleted.
1367
+ * Deletes a contact permanently and removes it from every audience it belongs to. Suppression records for the address are not affected: an unsubscribed or bounced address stays suppressed even after the contact is deleted.
1350
1368
  *
1351
1369
  */
1352
1370
  const deleteContact = (options) => (options.client ?? client).delete({
@@ -1364,7 +1382,8 @@ const deleteContact = (options) => (options.client ?? client).delete({
1364
1382
  /**
1365
1383
  * Get a contact
1366
1384
  *
1367
- * Returns a single contact by ID.
1385
+ * Returns a single contact, including its custom `data` values and the channels it can be reached on. To find a contact's ID by email address or `external_id`, use [List contacts](/docs/api/reference/list-contacts).
1386
+ *
1368
1387
  */
1369
1388
  const getContact = (options) => (options.client ?? client).get({
1370
1389
  security: [{
@@ -1381,7 +1400,9 @@ const getContact = (options) => (options.client ?? client).get({
1381
1400
  /**
1382
1401
  * Update a contact
1383
1402
  *
1384
- * Updates a contact. Supplied fields are changed; omitted fields are left unchanged. Custom values in `data` are merged keys you supply are set, keys set to null are removed, and keys you omit are unchanged. Changing the email address or `external_id` to a value already used by another contact returns a conflict error.
1403
+ * Updates a contact. Supplied fields are changed and omitted fields are left unchanged; set `first_name`, `last_name`, or `external_id` to null to clear them. Custom values in `data` are merged: keys you supply are set, keys set to null are removed, and keys you omit are unchanged.
1404
+ *
1405
+ * Changing the email address or `external_id` to a value already used by another contact returns a conflict error.
1385
1406
  *
1386
1407
  */
1387
1408
  const updateContact = (options) => (options.client ?? client).patch({
@@ -1403,7 +1424,8 @@ const updateContact = (options) => (options.client ?? client).patch({
1403
1424
  /**
1404
1425
  * List contact properties
1405
1426
  *
1406
- * Returns a paginated list of the workspace's contact properties.
1427
+ * Returns a paginated list of the workspace's contact properties, newest first. Archived properties are included; check each entry's `archived` flag.
1428
+ *
1407
1429
  */
1408
1430
  const listContactProperties = (options) => (options?.client ?? client).get({
1409
1431
  security: [{
@@ -1420,7 +1442,9 @@ const listContactProperties = (options) => (options?.client ?? client).get({
1420
1442
  /**
1421
1443
  * Create a contact property
1422
1444
  *
1423
- * Defines a custom property that contacts in the workspace can carry. The key becomes available in contact `data` and as a template variable in broadcasts. Keys are unique within the workspace; the key and type cannot be changed after creation.
1445
+ * Defines a custom property that contacts in the workspace can carry. The key becomes available in contact `data` and as a template variable in broadcasts. The key and type cannot be changed after creation.
1446
+ *
1447
+ * A key already in use returns a conflict error. A workspace can hold at most 200 properties; archived properties keep their key and count toward that limit.
1424
1448
  *
1425
1449
  */
1426
1450
  const createContactProperty = (options) => (options.client ?? client).post({
@@ -1442,7 +1466,8 @@ const createContactProperty = (options) => (options.client ?? client).post({
1442
1466
  /**
1443
1467
  * Get a contact property
1444
1468
  *
1445
- * Returns a single contact property by ID.
1469
+ * Returns a single contact property: its immutable key and type, the fallback value, and whether it is archived.
1470
+ *
1446
1471
  */
1447
1472
  const getContactProperty = (options) => (options.client ?? client).get({
1448
1473
  security: [{
@@ -1459,7 +1484,7 @@ const getContactProperty = (options) => (options.client ?? client).get({
1459
1484
  /**
1460
1485
  * Update a contact property
1461
1486
  *
1462
- * Updates a contact property's fallback value. The key and type cannot be changed after creation create a new property instead.
1487
+ * Updates a contact property's fallback value, the only mutable field. The key and type cannot be changed after creation; create a new property instead.
1463
1488
  *
1464
1489
  */
1465
1490
  const updateContactProperty = (options) => (options.client ?? client).patch({
@@ -1481,7 +1506,9 @@ const updateContactProperty = (options) => (options.client ?? client).patch({
1481
1506
  /**
1482
1507
  * Archive a contact property
1483
1508
  *
1484
- * Archives a contact property. The key stops being accepted in new contact writes and stops rendering in templates, but every value already stored on your contacts is preserved and still returned when you read a contact. The key stays reserved, so it cannot be re-created with a different type. Returns 409 if the property is already archived. Reverse it with unarchive.
1509
+ * Archives a contact property. The key stops being accepted in contact writes and stops rendering in templates, but every value already stored on your contacts is preserved and still returned when you read a contact.
1510
+ *
1511
+ * The key stays reserved and still counts toward the workspace's 200-property limit, so it cannot be re-created with a different type. Archiving an already-archived property returns a conflict error; reverse it with [Unarchive a contact property](/docs/api/reference/unarchive-contact-property).
1485
1512
  *
1486
1513
  */
1487
1514
  const archiveContactProperty = (options) => (options.client ?? client).post({
@@ -1499,7 +1526,7 @@ const archiveContactProperty = (options) => (options.client ?? client).post({
1499
1526
  /**
1500
1527
  * Unarchive a contact property
1501
1528
  *
1502
- * Reactivates an archived contact property. The key is accepted in contact writes and renders in templates again; stored values were never removed, so they are unchanged. Returns 409 if the property is not archived.
1529
+ * Reactivates an archived contact property. The key is accepted in contact writes and renders in templates again; stored values were never removed, so they are unchanged. Unarchiving a property that is not archived returns a conflict error.
1503
1530
  *
1504
1531
  */
1505
1532
  const unarchiveContactProperty = (options) => (options.client ?? client).post({
@@ -1517,7 +1544,7 @@ const unarchiveContactProperty = (options) => (options.client ?? client).post({
1517
1544
  /**
1518
1545
  * List audiences
1519
1546
  *
1520
- * Returns a paginated list of audiences in the workspace, newest first. Filter to audiences whose name contains a substring with `search`.
1547
+ * Returns a paginated list of audiences in the workspace, newest first. Filter to audiences whose name contains a substring with `q`.
1521
1548
  *
1522
1549
  */
1523
1550
  const listAudiences = (options) => (options?.client ?? client).get({
@@ -1535,7 +1562,7 @@ const listAudiences = (options) => (options?.client ?? client).get({
1535
1562
  /**
1536
1563
  * Create an audience
1537
1564
  *
1538
- * Creates an audience in the workspace. Static audiences start empty add contacts via the audience contacts endpoint or the bulk contact upsert.
1565
+ * Creates an audience in the workspace. New audiences start empty: add members with [Add contacts to an audience](/docs/api/reference/assign-audience-contacts) or through [Create or update contacts in bulk](/docs/api/reference/create-contact-batch). Only `static` audiences can be created today; requesting `dynamic` or `external` returns a validation error.
1539
1566
  *
1540
1567
  */
1541
1568
  const createAudience = (options) => (options.client ?? client).post({
@@ -1557,7 +1584,7 @@ const createAudience = (options) => (options.client ?? client).post({
1557
1584
  /**
1558
1585
  * Delete an audience
1559
1586
  *
1560
- * Deletes an audience and its memberships. Contacts themselves are not deleted. An audience cannot be deleted while a broadcast targeting it is scheduled, accepted, sending, or canceling cancel that broadcast first.
1587
+ * Deletes an audience and its memberships. Contacts themselves are not deleted. An audience cannot be deleted while a broadcast targeting it is scheduled, accepted, sending, or canceling; cancel that broadcast first, then retry.
1561
1588
  *
1562
1589
  */
1563
1590
  const deleteAudience = (options) => (options.client ?? client).delete({
@@ -1575,7 +1602,8 @@ const deleteAudience = (options) => (options.client ?? client).delete({
1575
1602
  /**
1576
1603
  * Get an audience
1577
1604
  *
1578
- * Returns a single audience by ID.
1605
+ * Returns a single audience: its name, description, and type. The member list is separate; fetch it with [List an audience's contacts](/docs/api/reference/list-audience-contacts).
1606
+ *
1579
1607
  */
1580
1608
  const getAudience = (options) => (options.client ?? client).get({
1581
1609
  security: [{
@@ -1592,7 +1620,8 @@ const getAudience = (options) => (options.client ?? client).get({
1592
1620
  /**
1593
1621
  * Update an audience
1594
1622
  *
1595
- * Updates an audience's name or description.
1623
+ * Updates an audience's name or description. Omitted fields are left unchanged; set `description` to null to clear it.
1624
+ *
1596
1625
  */
1597
1626
  const updateAudience = (options) => (options.client ?? client).patch({
1598
1627
  security: [{
@@ -1631,7 +1660,7 @@ const listAudienceContacts = (options) => (options.client ?? client).get({
1631
1660
  /**
1632
1661
  * Add contacts to an audience
1633
1662
  *
1634
- * Adds up to 1,000 contacts to a static audience. Contacts that are already members are left in place. If any contact ID does not exist, the whole request fails and no contacts are added.
1663
+ * Adds up to 1,000 contacts to an audience. Adding is idempotent: contacts that are already members are left in place and keep their original join time. If any contact ID does not exist in the workspace, the whole request fails with a validation error and no contacts are added.
1635
1664
  *
1636
1665
  */
1637
1666
  const assignAudienceContacts = (options) => (options.client ?? client).post({
@@ -1653,7 +1682,7 @@ const assignAudienceContacts = (options) => (options.client ?? client).post({
1653
1682
  /**
1654
1683
  * Remove contacts from an audience
1655
1684
  *
1656
- * Removes up to 1,000 contacts from a static audience. Contacts that are not members are skipped. If any contact ID does not exist, the whole request fails and no contacts are removed. The contacts themselves are not deleted and remain members of any other audiences.
1685
+ * Removes up to 1,000 contacts from an audience. Contacts that are not members are skipped. If any contact ID does not exist in the workspace, the whole request fails with a validation error and no memberships are removed. The contacts themselves are not deleted and remain members of any other audiences.
1657
1686
  *
1658
1687
  */
1659
1688
  const unassignAudienceContacts = (options) => (options.client ?? client).post({
@@ -1693,7 +1722,8 @@ const unassignAudienceContact = (options) => (options.client ?? client).delete({
1693
1722
  /**
1694
1723
  * List SMS messages
1695
1724
  *
1696
- * Returns a paginated list of SMS messages in the workspace, newest first.
1725
+ * Returns the workspace's SMS messages as a cursor-paginated list, newest first. Filter by direction, status, category, recipient, sender, failure reason, tag, or creation time; pass the response's `next_cursor` back as `starting_after` to fetch the next page. To follow a single message's delivery, use [Get an SMS message](/docs/api/reference/get-sms-message) instead.
1726
+ *
1697
1727
  */
1698
1728
  const listSmsMessages = (options) => (options?.client ?? client).get({
1699
1729
  security: [{
@@ -1710,7 +1740,22 @@ const listSmsMessages = (options) => (options?.client ?? client).get({
1710
1740
  /**
1711
1741
  * Send an SMS message
1712
1742
  *
1713
- * Sends a single SMS message to one recipient. The 202 response is returned only after the message is durably accepted for delivery; actual delivery happens asynchronously, and you track it with the get-message and list-events endpoints or with webhooks. `category` is required and controls opt-out (STOP) policy, quiet hours, and per-country compliance. A body exceeding the 12-segment cap is rejected with a 422, as are field-level validation failures and sends that cannot be afforded by the workspace balance.
1743
+ * Sends one SMS message to a single recipient. A send carries exactly one
1744
+ * content form: `text` (free text, which also requires `category`) or
1745
+ * `template` (a stored template that supplies the body and category). To
1746
+ * submit up to 100 independent messages in one request, use
1747
+ * [Send a batch of SMS messages](/docs/api/reference/create-sms-message-batch)
1748
+ * instead.
1749
+ *
1750
+ * The `202` response means Bird durably accepted the message for asynchronous
1751
+ * delivery, not that it was delivered. Follow delivery with
1752
+ * [Get an SMS message](/docs/api/reference/get-sms-message) or by subscribing
1753
+ * to `sms.*` webhook events.
1754
+ *
1755
+ * Sends fail with a `422` when a field is invalid, the body exceeds the
1756
+ * 12-segment cap, the destination country is not enabled for the workspace,
1757
+ * or the sender is not permitted for the destination; a send from a
1758
+ * workspace with no wallet balance fails with a `402`.
1714
1759
  *
1715
1760
  */
1716
1761
  const createSmsMessage = (options) => (options.client ?? client).post({
@@ -1732,7 +1777,16 @@ const createSmsMessage = (options) => (options.client ?? client).post({
1732
1777
  /**
1733
1778
  * Send a batch of SMS messages
1734
1779
  *
1735
- * Accepts up to 100 independent SMS messages and queues them for delivery. Each message is an independent send with its own ID, status, and cost. All items are validated before any are queued — if one fails validation, the entire batch is rejected. Field-level validation failures, the 12-segment body cap, and insufficient workspace balance all return 422 (or 402 for balance).
1780
+ * Sends up to 100 independent SMS messages in one request. Each item is a
1781
+ * complete send request with its own recipient, content, id, status, and
1782
+ * cost. For a single message, use
1783
+ * [Send an SMS message](/docs/api/reference/create-sms-message) instead.
1784
+ *
1785
+ * Acceptance is all-or-nothing: every item is validated before any is queued,
1786
+ * and one invalid item rejects the whole batch with a `422` (nothing is
1787
+ * sent). A batch from a workspace with no wallet balance fails with a `402`.
1788
+ * The `202` response lists the accepted messages in submission order; each
1789
+ * delivers asynchronously and is tracked individually, like a single send.
1736
1790
  *
1737
1791
  */
1738
1792
  const createSmsMessageBatch = (options) => (options.client ?? client).post({
@@ -1754,7 +1808,8 @@ const createSmsMessageBatch = (options) => (options.client ?? client).post({
1754
1808
  /**
1755
1809
  * Get an SMS message
1756
1810
  *
1757
- * Returns a single SMS message with its current status, segment breakdown, cost, and failure detail if it failed.
1811
+ * Returns a single SMS message: its current delivery status, segment breakdown, cost, and failure detail when it failed. The `status` advances asynchronously as delivery progresses, and `cost` is null until the message has been priced, so poll this endpoint (or subscribe to `sms.*` webhook events) after a send to confirm delivery. To scan messages in bulk, use [List SMS messages](/docs/api/reference/list-sms-messages) instead.
1812
+ *
1758
1813
  */
1759
1814
  const getSmsMessage = (options) => (options.client ?? client).get({
1760
1815
  security: [{
@@ -1771,7 +1826,7 @@ const getSmsMessage = (options) => (options.client ?? client).get({
1771
1826
  /**
1772
1827
  * List SMS templates
1773
1828
  *
1774
- * Returns the SMS templates available to your workspace, including Bird's built-in templates. Filter by scope, category, or language.
1829
+ * Returns the SMS templates you can send from, including Bird's built-in templates. Filter by scope, category, or language; the catalogue is small and returned in full, so this list is not paginated. To read one template's variables before sending with it, use [Get an SMS template](/docs/api/reference/get-sms-template).
1775
1830
  *
1776
1831
  */
1777
1832
  const listSmsTemplates = (options) => (options?.client ?? client).get({
@@ -1789,7 +1844,8 @@ const listSmsTemplates = (options) => (options?.client ?? client).get({
1789
1844
  /**
1790
1845
  * Get an SMS template
1791
1846
  *
1792
- * Returns a single SMS template by its name or id.
1847
+ * Returns a single SMS template: its body preview, category, the `variables` it expects (each with its accepted format), and the languages it is available in. Fetch a template before sending with it to see which `parameters` keys are required; an unknown reference returns a `404`. To browse the whole catalogue, use [List SMS templates](/docs/api/reference/list-sms-templates) instead.
1848
+ *
1793
1849
  */
1794
1850
  const getSmsTemplate = (options) => (options.client ?? client).get({
1795
1851
  security: [{
@@ -1806,7 +1862,11 @@ const getSmsTemplate = (options) => (options.client ?? client).get({
1806
1862
  /**
1807
1863
  * Create a verification
1808
1864
  *
1809
- * Creates a verification for a recipient and sends them a one-time passcode. Provide a recipient in `to` an email address (verified over email), a phone number (verified over SMS), or both. When you provide both, the passcode is sent over one channel and falls back to the other if that delivery fails; it is not sent to both at once. If a verification is already in progress for the same recipient, that one is reused and a fresh passcode is sent once the resend cooldown has elapsed, rather than starting a second verification. The response includes the verification's current state; the passcode itself is never returned. Submit the passcode the recipient enters with the check endpoint.
1865
+ * Creates a verification for a recipient and sends them a one-time passcode. Provide the recipient in `to`: an email address (verified over email), a phone number (verified over SMS), or both. With both, the passcode is sent over one channel at a time and delivery falls over to the other channel if the first fails; it is never sent to both at once.
1866
+ *
1867
+ * Calling this again for the same recipient resumes the verification in progress rather than starting a second one: within the resend cooldown the request returns the current state without sending, and after it a fresh passcode is sent. Use the same call to send and to resend.
1868
+ *
1869
+ * The `200` response is the verification's current state; the passcode itself is never returned. Submit the passcode the recipient enters with POST /v1/verify/verifications/check before the verification's `expires_at`. An invalid recipient returns `422`, and requesting passcodes for the same recipient too often returns `429`.
1810
1870
  *
1811
1871
  */
1812
1872
  const createVerification = (options) => (options.client ?? client).post({
@@ -1828,7 +1888,11 @@ const createVerification = (options) => (options.client ?? client).post({
1828
1888
  /**
1829
1889
  * Check a verification passcode
1830
1890
  *
1831
- * Checks a passcode for a recipient and returns the outcome together with the verification's current state. Identify the verification by the same `to` recipient used to create it you do not need to store a verification id. A wrong or expired passcode is reported in the response body, with `success` set to `false` and a `reason` such as `incorrect_code` or `expired`, rather than as an HTTP error. Only a missing verification — including one that has already reached a final state, which is no longer checkable — malformed input, or rate limiting return an error status.
1891
+ * Checks a passcode for a recipient and returns the outcome together with the verification's current state. Identify the verification by the same `to` used to create it; you do not need to store a verification ID.
1892
+ *
1893
+ * A wrong or expired passcode is a normal outcome, not an HTTP error: the response is `200` with `success` set to `false` and a `reason` such as `incorrect_code` or `expired`. `success: true` means the verification is complete. Each verification reports its final outcome exactly once and is no longer checkable afterwards.
1894
+ *
1895
+ * An error status is returned only when the check cannot be evaluated: `404` when no verification matches the recipient or the matching one already reached its final state, `422` for an invalid recipient, and `429` when passcodes for a recipient are checked too quickly.
1832
1896
  *
1833
1897
  */
1834
1898
  const createVerificationCheck = (options) => (options.client ?? client).post({
@@ -1850,7 +1914,8 @@ const createVerificationCheck = (options) => (options.client ?? client).post({
1850
1914
  /**
1851
1915
  * List WhatsApp messages
1852
1916
  *
1853
- * Returns a paginated list of WhatsApp messages in the workspace, newest first.
1917
+ * Returns the workspace's WhatsApp messages as a cursor-paginated list, newest first. Filter by status, contact phone number, business-scoped user ID, tag, or creation time; pass the response's `next_cursor` back as `starting_after` to fetch the next page. To follow a single message's delivery, use [Get a WhatsApp message](/docs/api/reference/get-whats-app-message) instead.
1918
+ *
1854
1919
  */
1855
1920
  const listWhatsAppMessages = (options) => (options?.client ?? client).get({
1856
1921
  security: [{
@@ -1865,9 +1930,27 @@ const listWhatsAppMessages = (options) => (options?.client ?? client).get({
1865
1930
  ...options
1866
1931
  });
1867
1932
  /**
1868
- * Send a message
1933
+ * Send a WhatsApp message
1934
+ *
1935
+ * Sends a WhatsApp message built from a message template to one recipient.
1936
+ * Name the template, optionally pick its language variant, and fill its
1937
+ * placeholders in `components`; Bird selects the sender number from the
1938
+ * template's category, so the request carries no sender field. Templates are
1939
+ * the only supported content type: a request without `template` is rejected
1940
+ * with a `422`. Browse what you can send with
1941
+ * [List available message templates](/docs/api/reference/list-whats-app-templates).
1942
+ *
1943
+ * The `202` response is the accepted message, echoing the resolved template
1944
+ * and language; it is not a delivery confirmation. Follow delivery with
1945
+ * [Get a WhatsApp message](/docs/api/reference/get-whats-app-message), the
1946
+ * per-message timeline from
1947
+ * [List events for a WhatsApp message](/docs/api/reference/list-whats-app-message-events),
1948
+ * or `whatsapp.*` webhook events.
1949
+ *
1950
+ * A template name or language the catalogue does not stock, parameter values
1951
+ * that do not match the template's declared placeholders, and a recipient
1952
+ * that is not a valid phone number each return a `422`.
1869
1953
  *
1870
- * Sends a template message. Bird selects the sender number from the template's category.
1871
1954
  */
1872
1955
  const sendWhatsAppMessage = (options) => (options.client ?? client).post({
1873
1956
  security: [{
@@ -1888,7 +1971,8 @@ const sendWhatsAppMessage = (options) => (options.client ?? client).post({
1888
1971
  /**
1889
1972
  * Get a WhatsApp message
1890
1973
  *
1891
- * Returns a single WhatsApp message with its current delivery status and failure detail if applicable.
1974
+ * Returns a single WhatsApp message: its current delivery status, per-stage timestamps (`sent_at`, `delivered_at`, `read_at`), the template it was sent from, and failure detail when it failed. The `status` advances asynchronously as delivery progresses, so poll this endpoint (or subscribe to `whatsapp.*` webhook events) after a send to confirm delivery. For the per-event timeline, use [List events for a WhatsApp message](/docs/api/reference/list-whats-app-message-events) instead.
1975
+ *
1892
1976
  */
1893
1977
  const getWhatsAppMessage = (options) => (options.client ?? client).get({
1894
1978
  security: [{
@@ -1905,7 +1989,8 @@ const getWhatsAppMessage = (options) => (options.client ?? client).get({
1905
1989
  /**
1906
1990
  * List events for a WhatsApp message
1907
1991
  *
1908
- * Returns the lifecycle event timeline for a WhatsApp message, in chronological order.
1992
+ * Returns a WhatsApp message's lifecycle events in chronological order, one entry per delivery transition (`whatsapp.accepted`, `whatsapp.sent`, `whatsapp.delivered`, `whatsapp.read`, `whatsapp.failed`). The timeline is bounded and returned in full, so this list is not paginated; an unknown message id returns a `404`. For the message's current state in a single field, use [Get a WhatsApp message](/docs/api/reference/get-whats-app-message) instead.
1993
+ *
1909
1994
  */
1910
1995
  const listWhatsAppMessageEvents = (options) => (options.client ?? client).get({
1911
1996
  security: [{
@@ -1922,7 +2007,8 @@ const listWhatsAppMessageEvents = (options) => (options.client ?? client).get({
1922
2007
  /**
1923
2008
  * List available message templates
1924
2009
  *
1925
- * Returns the message templates available to this workspace.
2010
+ * Returns the WhatsApp message templates you can send from. Each entry carries the template's `name` (the reference you pass when sending), its language, category, review status, and its content blocks with example values for every placeholder. The catalogue is returned in full, so this list is not paginated, and it can differ by region: you see the templates stocked for the region you call. Pick a template here, then fill its placeholders in the `components` of [Send a WhatsApp message](/docs/api/reference/send-whats-app-message).
2011
+ *
1926
2012
  */
1927
2013
  const listWhatsAppTemplates = (options) => (options?.client ?? client).get({
1928
2014
  security: [{
@@ -1937,9 +2023,362 @@ const listWhatsAppTemplates = (options) => (options?.client ?? client).get({
1937
2023
  ...options
1938
2024
  });
1939
2025
  /**
2026
+ * Daily sending statistics
2027
+ *
2028
+ * Returns one row of aggregate sending statistics per calendar day for the workspace: UTC days by default, or your local days when `timezone` is set. Days with no activity are included with zero counts, so the series charts without client-side gap handling. Suited to charts and trend lines; for per-message exact accounting use the message detail endpoints.
2029
+ *
2030
+ * Rows are bucketed by event time, not send time: a complaint received on Wednesday for a message sent the prior Monday is counted in Wednesday's row.
2031
+ *
2032
+ * The maximum window is 365 days; requesting a longer range returns 422.
2033
+ *
2034
+ */
2035
+ const getEmailStatsDaily = (options) => (options?.client ?? client).get({
2036
+ security: [{
2037
+ scheme: "bearer",
2038
+ type: "http"
2039
+ }, {
2040
+ in: "cookie",
2041
+ name: "bird_session",
2042
+ type: "apiKey"
2043
+ }],
2044
+ url: "/v1/email/stats/daily",
2045
+ ...options
2046
+ });
2047
+ /**
2048
+ * Hourly sending statistics
2049
+ *
2050
+ * Returns one row of aggregate sending statistics per hour for the workspace: UTC hours by default, or your local hours when `timezone` is set (a timezone with a sub-hour offset gets correctly aligned hours). Useful for inspecting send rate, deliverability, and engagement inside a single day or a recent window; hours with no activity are included with zero counts.
2051
+ *
2052
+ * Rows are bucketed by event time, not send time: a click recorded at 14:07 for a message sent at 09:00 lands in the 14:00 row.
2053
+ *
2054
+ * A single request may span at most 30 days (720 hourly rows); for longer ranges use the daily endpoint, which has a 365-day window. An hourly window longer than 30 days, or a `from` after `to`, returns 422.
2055
+ *
2056
+ */
2057
+ const getEmailStatsHourly = (options) => (options?.client ?? client).get({
2058
+ security: [{
2059
+ scheme: "bearer",
2060
+ type: "http"
2061
+ }, {
2062
+ in: "cookie",
2063
+ name: "bird_session",
2064
+ type: "apiKey"
2065
+ }],
2066
+ url: "/v1/email/stats/hourly",
2067
+ ...options
2068
+ });
2069
+ /**
2070
+ * Stats by tag
2071
+ *
2072
+ * Returns aggregate delivery and engagement counts grouped by tag for the requested period. Rows are ranked by the `sort` metric (default `processed`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare campaign performance across tags you set at send time.
2073
+ *
2074
+ * Rows are computed against event time (not send time), so engagement received during the period for messages sent earlier is included.
2075
+ *
2076
+ * The maximum window is 365 days; requesting a longer range returns 422.
2077
+ *
2078
+ */
2079
+ const getEmailStatsByTag = (options) => (options?.client ?? client).get({
2080
+ security: [{
2081
+ scheme: "bearer",
2082
+ type: "http"
2083
+ }, {
2084
+ in: "cookie",
2085
+ name: "bird_session",
2086
+ type: "apiKey"
2087
+ }],
2088
+ url: "/v1/email/stats/tags",
2089
+ ...options
2090
+ });
2091
+ /**
2092
+ * Aggregate stats summary
2093
+ *
2094
+ * Returns a single-row aggregate across the requested period covering delivery, bounce, complaint, open, and click counts plus the derived rates, along with processing, delivery, and total latency percentiles (p50/p95/p99). Suitable for KPI tiles, campaign reports, and email digests; the daily and hourly endpoints carry the same metrics per time bucket.
2095
+ *
2096
+ * The aggregate is computed against event time (not send time), so engagement received during the period for messages sent earlier is included. Rate fields are null when their denominator is zero.
2097
+ *
2098
+ * The window grain follows the form of `from` and `to`: calendar days (`YYYY-MM-DD`, up to 365 days) or RFC 3339 instants (hour grain, up to 720 hours, 30 days), so a rolling window such as the last 24 hours is a single request. Mixing the two forms returns 422. Set `timezone` to compute day and hour boundaries in a local zone instead of UTC, and `compare=previous_period` to include the preceding equal-length window in the same response.
2099
+ *
2100
+ */
2101
+ const getEmailStatsSummary = (options) => (options?.client ?? client).get({
2102
+ security: [{
2103
+ scheme: "bearer",
2104
+ type: "http"
2105
+ }, {
2106
+ in: "cookie",
2107
+ name: "bird_session",
2108
+ type: "apiKey"
2109
+ }],
2110
+ url: "/v1/email/stats/summary",
2111
+ ...options
2112
+ });
2113
+ /**
2114
+ * Stats by sending IP
2115
+ *
2116
+ * Returns delivery and deliverability counts grouped by the specific IP address used to send each message. Use this to identify per-IP reputation issues: block bounces concentrated on a single IP usually indicate a reputation problem on that IP, and `sort=bounces.block` surfaces those IPs first.
2117
+ *
2118
+ * A sending IP is known only once the upstream mail system reports delivery, bounce, deferral, or a late bounce, so rows cover the delivery stage onward: `accepted` and `processed` counts, processing latency, engagement, and complaint attribution are not available per IP. For workspace-wide figures use `GET /v1/email/stats/daily`. Rows are computed against event time (not send time).
2119
+ *
2120
+ * Rows are ranked by the `sort` field (default `delivered`) descending and capped at the requested `limit` (default 50, hard maximum 200). The maximum window is 365 days; requesting a longer range returns 422.
2121
+ *
2122
+ */
2123
+ const getEmailStatsBySendingIp = (options) => (options?.client ?? client).get({
2124
+ security: [{
2125
+ scheme: "bearer",
2126
+ type: "http"
2127
+ }, {
2128
+ in: "cookie",
2129
+ name: "bird_session",
2130
+ type: "apiKey"
2131
+ }],
2132
+ url: "/v1/email/stats/sending-ips",
2133
+ ...options
2134
+ });
2135
+ /**
2136
+ * Stats by sending domain
2137
+ *
2138
+ * Returns delivery, engagement, and deliverability counts grouped by sending domain (the portion of the `From` address after the `@`). Use this to compare deliverability across multiple verified domains in your workspace, for example transactional versus marketing domains, or sub-domain segregation during IP warming.
2139
+ *
2140
+ * Rows are computed against event time (not send time), so engagement and bounces received during the period for messages sent earlier are included.
2141
+ *
2142
+ * Rows are ranked by the `sort` metric (default `processed`) descending and capped at the requested `limit` (default 50, hard maximum 200). The maximum window is 365 days; requesting a longer range returns 422.
2143
+ *
2144
+ */
2145
+ const getEmailStatsBySendingDomain = (options) => (options?.client ?? client).get({
2146
+ security: [{
2147
+ scheme: "bearer",
2148
+ type: "http"
2149
+ }, {
2150
+ in: "cookie",
2151
+ name: "bird_session",
2152
+ type: "apiKey"
2153
+ }],
2154
+ url: "/v1/email/stats/sending-domains",
2155
+ ...options
2156
+ });
2157
+ /**
2158
+ * Stats by category
2159
+ *
2160
+ * Returns aggregate delivery and engagement counts grouped by category for the requested period. Rows are ranked by the `sort` metric (default `processed`) descending and capped at the requested `limit` (default 50, hard maximum 200). Use this to compare deliverability and engagement between your transactional and marketing traffic.
2161
+ *
2162
+ * Rows are computed against event time (not send time), so engagement received during the period for messages sent earlier is included.
2163
+ *
2164
+ * The maximum window is 365 days; requesting a longer range returns 422.
2165
+ *
2166
+ */
2167
+ const getEmailStatsByCategory = (options) => (options?.client ?? client).get({
2168
+ security: [{
2169
+ scheme: "bearer",
2170
+ type: "http"
2171
+ }, {
2172
+ in: "cookie",
2173
+ name: "bird_session",
2174
+ type: "apiKey"
2175
+ }],
2176
+ url: "/v1/email/stats/categories",
2177
+ ...options
2178
+ });
2179
+ /**
2180
+ * Stats by mailbox provider
2181
+ *
2182
+ * Returns delivery, engagement, and deliverability counts grouped by recipient mailbox provider (for example `gmail`, `yahoo`, `microsoft`, `apple`): the deliverability-by-inbox-provider view. Use this to compare how each major inbox provider treats your mail, for example to spot a delivered-rate dip or complaint spike at one provider before it spreads; for a per-region split within a provider, use the mailbox-provider-region breakdown.
2183
+ *
2184
+ * A recipient's mailbox provider is known only once the receiving mail system reports an outcome, so rows cover the delivery stage onward: `accepted`, `processed`, and `rejected` counts and processing latency are not included. Rows are computed against event time (not send time).
2185
+ *
2186
+ * Rows are ranked by the `sort` metric (default `delivered`) descending and capped at the requested `limit` (default 50, hard maximum 200). The maximum window is 365 days; requesting a longer range returns 422.
2187
+ *
2188
+ */
2189
+ const getEmailStatsByMailboxProvider = (options) => (options?.client ?? client).get({
2190
+ security: [{
2191
+ scheme: "bearer",
2192
+ type: "http"
2193
+ }, {
2194
+ in: "cookie",
2195
+ name: "bird_session",
2196
+ type: "apiKey"
2197
+ }],
2198
+ url: "/v1/email/stats/mailbox-providers",
2199
+ ...options
2200
+ });
2201
+ /**
2202
+ * Stats by mailbox provider region
2203
+ *
2204
+ * Returns delivery, engagement, and deliverability counts grouped by mailbox provider and provider region pair, for example `gmail` in `NA` or `microsoft` in `EU`. The provider region is the regional pod the receiving mail system reports for the recipient's provider; pairing it with the provider disambiguates a region label that several providers share. Use this to spot a deliverability problem isolated to one provider in one region; for a per-provider view without the region split, use the mailbox-provider breakdown.
2205
+ *
2206
+ * A provider region is known only once the receiving mail system reports an outcome, so rows cover the delivery stage onward: `accepted`, `processed`, and `rejected` counts and processing latency are not included. Rows are computed against event time (not send time).
2207
+ *
2208
+ * Rows are ranked by the `sort` metric (default `delivered`) descending and capped at the requested `limit` (default 50, hard maximum 200). The maximum window is 365 days; requesting a longer range returns 422.
2209
+ *
2210
+ */
2211
+ const getEmailStatsByMailboxProviderRegion = (options) => (options?.client ?? client).get({
2212
+ security: [{
2213
+ scheme: "bearer",
2214
+ type: "http"
2215
+ }, {
2216
+ in: "cookie",
2217
+ name: "bird_session",
2218
+ type: "apiKey"
2219
+ }],
2220
+ url: "/v1/email/stats/mailbox-provider-regions",
2221
+ ...options
2222
+ });
2223
+ /**
2224
+ * Stats by recipient domain
2225
+ *
2226
+ * Returns aggregate delivery and engagement counts grouped by recipient mailbox domain (the part of each recipient address after the `@`, for example `gmail.com`, `yahoo.com`, `outlook.com`) for the requested period. This is the finest-grained deliverability view: where the mailbox-provider breakdown groups recipients into provider buckets such as `gmail` or `microsoft`, this keys on the exact destination domain. Use it to spot a delivery-rate dip or complaint spike at a specific domain.
2227
+ *
2228
+ * Rows are ranked by the `sort` metric (default `processed`) descending and capped at the requested `limit` (default 50, hard maximum 200). Rows are computed against event time (not send time), so engagement received during the period for messages sent earlier is included.
2229
+ *
2230
+ * The maximum window is 365 days; requesting a longer range returns 422.
2231
+ *
2232
+ */
2233
+ const getEmailStatsByRecipientDomain = (options) => (options?.client ?? client).get({
2234
+ security: [{
2235
+ scheme: "bearer",
2236
+ type: "http"
2237
+ }, {
2238
+ in: "cookie",
2239
+ name: "bird_session",
2240
+ type: "apiKey"
2241
+ }],
2242
+ url: "/v1/email/stats/recipient-domains",
2243
+ ...options
2244
+ });
2245
+ /**
2246
+ * Stats by template
2247
+ *
2248
+ * Returns aggregate delivery and engagement counts grouped by the template each message was sent with, so a template's deliverability and engagement can be compared side by side. Attribution is by the template used at send time; only messages sent with a template appear here, so a workspace that has sent none returns an empty list rather than an error. Each row is keyed by the template ID (`emt_…`); a template deleted after sending still appears by its ID.
2249
+ *
2250
+ * Rows are ranked by the `sort` metric (default `processed`) descending and capped at the requested `limit` (default 50, hard maximum 200). Rows are computed against event time (not send time), so engagement received during the period for messages sent earlier is included.
2251
+ *
2252
+ * The maximum window is 365 days; requesting a longer range returns 422.
2253
+ *
2254
+ */
2255
+ const getEmailStatsByTemplate = (options) => (options?.client ?? client).get({
2256
+ security: [{
2257
+ scheme: "bearer",
2258
+ type: "http"
2259
+ }, {
2260
+ in: "cookie",
2261
+ name: "bird_session",
2262
+ type: "apiKey"
2263
+ }],
2264
+ url: "/v1/email/stats/templates",
2265
+ ...options
2266
+ });
2267
+ /**
2268
+ * Engagement by location
2269
+ *
2270
+ * Returns engagement counts (opens and clicks) grouped by the location they were recorded from, for the requested period. Use it to see where your audience engages, for example the top countries by unique opens. Location is known from open and click events only, so rows carry engagement counts but no delivery counts or rates.
2271
+ *
2272
+ * Use `group_by` to choose the granularity: `country` (default), `region`, or `city`. Each row carries the location hierarchy down to the requested level (a `city` grouping also reports the row's region and country). Rows are ranked by the `sort` metric (default `unique_opens`) descending and capped at the requested `limit` (default 50, hard maximum 200).
2273
+ *
2274
+ * Rows are computed against event time (not send time). The maximum window is 365 days; requesting a longer range returns 422.
2275
+ *
2276
+ */
2277
+ const getEmailStatsByLocation = (options) => (options?.client ?? client).get({
2278
+ security: [{
2279
+ scheme: "bearer",
2280
+ type: "http"
2281
+ }, {
2282
+ in: "cookie",
2283
+ name: "bird_session",
2284
+ type: "apiKey"
2285
+ }],
2286
+ url: "/v1/email/stats/locations",
2287
+ ...options
2288
+ });
2289
+ /**
2290
+ * Engagement by email client
2291
+ *
2292
+ * Returns engagement counts (opens and clicks) grouped by the email client, operating system, or device type they were recorded from, for the requested period. Use it for the classic "opens by mail client" view, for example the share of opens from Apple Mail versus Gmail versus Outlook. The reading environment is known from open and click events only, so rows carry engagement counts but no delivery counts or rates.
2293
+ *
2294
+ * Use `group_by` to choose the facet: `email_client` (default), `os`, or `device_type`. Each row populates the chosen facet and leaves the other two null. Rows are ranked by the `sort` metric (default `unique_opens`) descending and capped at the requested `limit` (default 50, hard maximum 200).
2295
+ *
2296
+ * Rows are computed against event time (not send time). The maximum window is 365 days; requesting a longer range returns 422.
2297
+ *
2298
+ */
2299
+ const getEmailStatsByClient = (options) => (options?.client ?? client).get({
2300
+ security: [{
2301
+ scheme: "bearer",
2302
+ type: "http"
2303
+ }, {
2304
+ in: "cookie",
2305
+ name: "bird_session",
2306
+ type: "apiKey"
2307
+ }],
2308
+ url: "/v1/email/stats/clients",
2309
+ ...options
2310
+ });
2311
+ /**
2312
+ * Bounces by SMTP error code
2313
+ *
2314
+ * Returns bounce counts grouped by the SMTP error code the receiving mail server returned, for the requested period: the deliverability-debugging view that answers "which SMTP responses are driving my bounces". Each row reports the bounced recipients for one code plus the hard/soft/admin/block/undetermined split.
2315
+ *
2316
+ * This breakdown reports the failure side only: it has no delivered, open, click, or rate fields, because a bounce code is recorded only on bounce events.
2317
+ *
2318
+ * Rows are ranked by the `sort` metric (default `bounced`) descending, capped at the requested `limit` (default 50, hard maximum 200), and computed against event time (not send time). The maximum window is 365 days; requesting a longer range returns 422.
2319
+ *
2320
+ */
2321
+ const getEmailStatsByBounceCode = (options) => (options?.client ?? client).get({
2322
+ security: [{
2323
+ scheme: "bearer",
2324
+ type: "http"
2325
+ }, {
2326
+ in: "cookie",
2327
+ name: "bird_session",
2328
+ type: "apiKey"
2329
+ }],
2330
+ url: "/v1/email/stats/bounce-codes",
2331
+ ...options
2332
+ });
2333
+ /**
2334
+ * Complaints by type
2335
+ *
2336
+ * Returns spam-complaint counts grouped by the feedback-loop complaint type reported by the mailbox provider (for example `abuse`, `fraud`, `virus`), for the requested period. Use it to understand what kind of complaints your mail attracts.
2337
+ *
2338
+ * This breakdown reports the complaint side only: each row carries the complained count for one type and nothing else, because a complaint type is recorded only on spam-complaint events.
2339
+ *
2340
+ * Rows are ranked by `complained` descending, capped at the requested `limit` (default 50, hard maximum 200), and computed against event time (not send time). The maximum window is 365 days; requesting a longer range returns 422.
2341
+ *
2342
+ */
2343
+ const getEmailStatsByComplaintType = (options) => (options?.client ?? client).get({
2344
+ security: [{
2345
+ scheme: "bearer",
2346
+ type: "http"
2347
+ }, {
2348
+ in: "cookie",
2349
+ name: "bird_session",
2350
+ type: "apiKey"
2351
+ }],
2352
+ url: "/v1/email/stats/complaint-types",
2353
+ ...options
2354
+ });
2355
+ /**
2356
+ * Stats by broadcast
2357
+ *
2358
+ * Returns aggregate delivery and engagement counts grouped by broadcast for the requested period, so each broadcast's deliverability and engagement can be compared side by side. Only messages sent as part of a broadcast appear here; one-off and transactional sends are not included, so a workspace that has not sent broadcasts returns an empty list rather than an error.
2359
+ *
2360
+ * Rows are ranked by the `sort` metric (default `processed`) descending and capped at the requested `limit` (default 50, hard maximum 200). Rows are computed against event time (not send time), so engagement received during the period for messages sent earlier is included.
2361
+ *
2362
+ * The maximum window is 365 days; requesting a longer range returns 422. This breakdown is computed from per-message activity retained for 30 days, so it reflects roughly the last 30 days of activity even when the requested window reaches further back.
2363
+ *
2364
+ */
2365
+ const getEmailStatsByBroadcast = (options) => (options?.client ?? client).get({
2366
+ security: [{
2367
+ scheme: "bearer",
2368
+ type: "http"
2369
+ }, {
2370
+ in: "cookie",
2371
+ name: "bird_session",
2372
+ type: "apiKey"
2373
+ }],
2374
+ url: "/v1/email/stats/broadcasts",
2375
+ ...options
2376
+ });
2377
+ /**
1940
2378
  * List sending domains
1941
2379
  *
1942
- * Returns all sending domains for the current workspace, ordered by creation date descending.
2380
+ * Returns all sending domains for the current workspace, newest first by default. Each item is the full domain object, including capability statuses and `dns_records`, so no per-domain follow-up read is needed. Filter with `name` to find a specific domain.
2381
+ *
1943
2382
  */
1944
2383
  const listDomains = (options) => (options?.client ?? client).get({
1945
2384
  security: [{
@@ -1956,7 +2395,20 @@ const listDomains = (options) => (options?.client ?? client).get({
1956
2395
  /**
1957
2396
  * Add a sending domain
1958
2397
  *
1959
- * Registers a new sending domain and returns the DNS records required for verification: a DKIM TXT record, a return-path CNAME (which also covers SPF — no separate SPF record is needed), a DMARC policy, and, when a tracking domain is configured, a tracking CNAME. The domain starts in `pending` status; records are checked automatically once published, or on demand via the verify endpoint.
2398
+ * Registers a new sending domain and returns the DNS records to publish
2399
+ * for it. The DKIM TXT record proves ownership, and together with the
2400
+ * return-path CNAME (which also covers SPF; no separate SPF record is
2401
+ * needed) and a DMARC policy it gates sending. The tracking CNAME is
2402
+ * optional and gates branded link tracking only. Publish the records at
2403
+ * your DNS provider, then check progress with
2404
+ * [Trigger domain verification](/docs/api/reference/verify-domain); Bird
2405
+ * also re-checks published records automatically. Setup walkthrough:
2406
+ * [Sending domains](/docs/guides/email/sending-domains).
2407
+ *
2408
+ * The domain starts in `pending` status. A domain already registered in
2409
+ * this workspace returns `409`, and creation beyond your organization's
2410
+ * domain quota returns `422` `E10000`. A domain that never verifies
2411
+ * ownership is removed after about 14 days, with a reminder email first.
1960
2412
  *
1961
2413
  */
1962
2414
  const createDomain = (options) => (options.client ?? client).post({
@@ -1996,7 +2448,8 @@ const deleteDomain = (options) => (options.client ?? client).delete({
1996
2448
  /**
1997
2449
  * Get a sending domain
1998
2450
  *
1999
- * Returns the domain with current DNS verification status per record.
2451
+ * Returns the domain with its capability statuses and every DNS record's current verification state. This read reports the stored result of the last check; to run a fresh DNS check, use [Trigger domain verification](/docs/api/reference/verify-domain).
2452
+ *
2000
2453
  */
2001
2454
  const getDomain = (options) => (options.client ?? client).get({
2002
2455
  security: [{
@@ -2013,7 +2466,18 @@ const getDomain = (options) => (options.client ?? client).get({
2013
2466
  /**
2014
2467
  * Update a sending domain
2015
2468
  *
2016
- * Updates settings and configuration on a sending domain. `settings` changes apply immediately. Changes to `return_path`, `tracking`, or `dkim` on a verified capability are staged: the current configuration keeps serving until the new one's DNS records verify, then the change is promoted automatically. Staged values are visible under `capabilities.*.pending`; the records to publish appear in `dns_records` with `state: pending`.
2469
+ * Updates settings and configuration on a sending domain. `settings`
2470
+ * changes apply immediately. Changes to `return_path`, `tracking`, or
2471
+ * `dkim` on a verified capability are staged: the current configuration
2472
+ * keeps serving until the new one's DNS records verify, then the change
2473
+ * is promoted automatically. Staged values are visible under
2474
+ * `capabilities.*.pending`; the records to publish appear in
2475
+ * `dns_records` with `state: pending`.
2476
+ *
2477
+ * Invalid combinations are rejected: enabling tracking toggles without a
2478
+ * tracking domain, or removing the tracking domain while a toggle is on,
2479
+ * returns `409`; enabling inbound receiving has verification
2480
+ * prerequisites that return `422`. Each rule is detailed on its field.
2017
2481
  *
2018
2482
  */
2019
2483
  const updateDomain = (options) => (options.client ?? client).patch({
@@ -2035,7 +2499,17 @@ const updateDomain = (options) => (options.client ?? client).patch({
2035
2499
  /**
2036
2500
  * Trigger domain verification
2037
2501
  *
2038
- * Triggers an immediate DNS check and returns the updated verification result. Rate-limited to prevent DNS abuse (max 5 calls per domain per hour).
2502
+ * Runs a fresh DNS check across the domain's records (DKIM, return path,
2503
+ * DMARC, tracking, inbound MX, and any staged changes) and returns the
2504
+ * updated domain. Use it for an immediate result after publishing or
2505
+ * correcting records; [Get a sending domain](/docs/api/reference/get-domain)
2506
+ * only reports the last stored result, and Bird re-checks published
2507
+ * records automatically in the background.
2508
+ *
2509
+ * A `200` with records still `pending` is not a failure: the records were
2510
+ * not found yet, which is normal while DNS propagates (minutes to hours).
2511
+ * Recently verified records are not re-queried, so the call is safe to
2512
+ * repeat while you wait.
2039
2513
  *
2040
2514
  */
2041
2515
  const verifyDomain = (options) => (options.client ?? client).post({
@@ -2090,12 +2564,319 @@ function mergeHeaders(idempotencyKey, extra) {
2090
2564
  };
2091
2565
  }
2092
2566
  //#endregion
2567
+ //#region src/resources/emailStats.ts
2568
+ /**
2569
+ * `bird.email.stats` — read-only email statistics. Every method takes an
2570
+ * optional query object (window, timezone, and — for breakdowns — `limit` and
2571
+ * `sort`) and resolves the typed aggregate or breakdown for that window. These
2572
+ * are point reads, not cursor lists, so each returns an `APIPromise`, not a
2573
+ * paginated iterator. Reached as `bird.email.stats.*`.
2574
+ */
2575
+ var EmailStatsResource = class extends Resource {
2576
+ /**
2577
+ * Aggregate delivery, engagement, and latency for a window. Pass
2578
+ * `compare: "previous_period"` to also get the preceding window and the
2579
+ * change between the two.
2580
+ *
2581
+ * @example Summary for a month
2582
+ * const s = await bird.email.stats.summary({ from: "2026-05-01", to: "2026-05-31" });
2583
+ * console.log(s.sends_accepted, s.delivery.delivered);
2584
+ */
2585
+ summary(query, options) {
2586
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsSummary({
2587
+ client: this.client,
2588
+ query,
2589
+ headers,
2590
+ signal
2591
+ }));
2592
+ }
2593
+ /**
2594
+ * Daily time series — one row per calendar day in the window.
2595
+ *
2596
+ * @example Per-day series for a month
2597
+ * const series = await bird.email.stats.daily({ from: "2026-05-01", to: "2026-05-31" });
2598
+ * for (const row of series.data) console.log(row.bucket, row.delivery.delivered);
2599
+ */
2600
+ daily(query, options) {
2601
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsDaily({
2602
+ client: this.client,
2603
+ query,
2604
+ headers,
2605
+ signal
2606
+ }));
2607
+ }
2608
+ /**
2609
+ * Hourly time series — one row per hour in the window (max 720 hours).
2610
+ *
2611
+ * @example Per-hour series for a day
2612
+ * const series = await bird.email.stats.hourly({ from: "2026-05-01", to: "2026-05-02" });
2613
+ * for (const row of series.data) console.log(row.bucket, row.delivery.delivered);
2614
+ */
2615
+ hourly(query, options) {
2616
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsHourly({
2617
+ client: this.client,
2618
+ query,
2619
+ headers,
2620
+ signal
2621
+ }));
2622
+ }
2623
+ /**
2624
+ * Breakdown by tag, ranked by `sort` (default `processed`) descending.
2625
+ *
2626
+ * @example Top 10 tags by delivered
2627
+ * const { data } = await bird.email.stats.byTag({
2628
+ * from: "2026-05-01",
2629
+ * to: "2026-05-31",
2630
+ * sort: "delivered",
2631
+ * limit: 10,
2632
+ * });
2633
+ * for (const row of data) console.log(row.tag, row.delivery.delivered);
2634
+ */
2635
+ byTag(query, options) {
2636
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByTag({
2637
+ client: this.client,
2638
+ query,
2639
+ headers,
2640
+ signal
2641
+ }));
2642
+ }
2643
+ /**
2644
+ * Breakdown by category (`transactional` / `marketing`).
2645
+ *
2646
+ * @example By category for a month
2647
+ * const { data } = await bird.email.stats.byCategory({ from: "2026-05-01", to: "2026-05-31" });
2648
+ * for (const row of data) console.log(row.category, row.delivery.delivered);
2649
+ */
2650
+ byCategory(query, options) {
2651
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByCategory({
2652
+ client: this.client,
2653
+ query,
2654
+ headers,
2655
+ signal
2656
+ }));
2657
+ }
2658
+ /**
2659
+ * Breakdown by sending IP, ranked by `sort` (default `delivered`) descending.
2660
+ *
2661
+ * @example IPs with the most block bounces
2662
+ * const { data } = await bird.email.stats.bySendingIp({
2663
+ * from: "2026-05-01",
2664
+ * to: "2026-05-31",
2665
+ * sort: "bounces.block",
2666
+ * limit: 20,
2667
+ * });
2668
+ * for (const row of data) console.log(row.sending_ip, row.delivery.delivered);
2669
+ */
2670
+ bySendingIp(query, options) {
2671
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsBySendingIp({
2672
+ client: this.client,
2673
+ query,
2674
+ headers,
2675
+ signal
2676
+ }));
2677
+ }
2678
+ /**
2679
+ * Breakdown by sending domain.
2680
+ *
2681
+ * @example By sending domain
2682
+ * const { data } = await bird.email.stats.bySendingDomain({
2683
+ * from: "2026-05-01",
2684
+ * to: "2026-05-31",
2685
+ * sort: "delivery_rate",
2686
+ * limit: 25,
2687
+ * });
2688
+ * for (const row of data) console.log(row.sending_domain, row.delivery.delivery_rate);
2689
+ */
2690
+ bySendingDomain(query, options) {
2691
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsBySendingDomain({
2692
+ client: this.client,
2693
+ query,
2694
+ headers,
2695
+ signal
2696
+ }));
2697
+ }
2698
+ /**
2699
+ * Breakdown by recipient mailbox domain (e.g. `gmail.com`).
2700
+ *
2701
+ * @example Recipient domains with the highest bounce rate
2702
+ * const { data } = await bird.email.stats.byRecipientDomain({
2703
+ * from: "2026-05-01",
2704
+ * to: "2026-05-31",
2705
+ * sort: "bounce_rate",
2706
+ * limit: 25,
2707
+ * });
2708
+ * for (const row of data) console.log(row.recipient_domain, row.delivery.bounce_rate);
2709
+ */
2710
+ byRecipientDomain(query, options) {
2711
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByRecipientDomain({
2712
+ client: this.client,
2713
+ query,
2714
+ headers,
2715
+ signal
2716
+ }));
2717
+ }
2718
+ /**
2719
+ * Breakdown by mailbox provider (e.g. Google, Microsoft).
2720
+ *
2721
+ * @example By mailbox provider
2722
+ * const { data } = await bird.email.stats.byMailboxProvider({
2723
+ * from: "2026-05-01",
2724
+ * to: "2026-05-31",
2725
+ * limit: 25,
2726
+ * });
2727
+ * for (const row of data) console.log(row.mailbox_provider, row.delivery.delivered);
2728
+ */
2729
+ byMailboxProvider(query, options) {
2730
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByMailboxProvider({
2731
+ client: this.client,
2732
+ query,
2733
+ headers,
2734
+ signal
2735
+ }));
2736
+ }
2737
+ /**
2738
+ * Breakdown by mailbox provider and region.
2739
+ *
2740
+ * @example By mailbox provider and region
2741
+ * const { data } = await bird.email.stats.byMailboxProviderRegion({
2742
+ * from: "2026-05-01",
2743
+ * to: "2026-05-31",
2744
+ * limit: 25,
2745
+ * });
2746
+ * for (const row of data) console.log(row.mailbox_provider, row.mailbox_provider_region, row.delivery.delivered);
2747
+ */
2748
+ byMailboxProviderRegion(query, options) {
2749
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByMailboxProviderRegion({
2750
+ client: this.client,
2751
+ query,
2752
+ headers,
2753
+ signal
2754
+ }));
2755
+ }
2756
+ /**
2757
+ * Breakdown by template (by `emt_…` ID or name).
2758
+ *
2759
+ * @example By template
2760
+ * const { data } = await bird.email.stats.byTemplate({
2761
+ * from: "2026-05-01",
2762
+ * to: "2026-05-31",
2763
+ * sort: "open_rate",
2764
+ * limit: 25,
2765
+ * });
2766
+ * for (const row of data) console.log(row.template_id, row.engagement.open_rate);
2767
+ */
2768
+ byTemplate(query, options) {
2769
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByTemplate({
2770
+ client: this.client,
2771
+ query,
2772
+ headers,
2773
+ signal
2774
+ }));
2775
+ }
2776
+ /**
2777
+ * Breakdown by recipient geographic location.
2778
+ *
2779
+ * @example By location
2780
+ * const { data } = await bird.email.stats.byLocation({
2781
+ * from: "2026-05-01",
2782
+ * to: "2026-05-31",
2783
+ * limit: 25,
2784
+ * });
2785
+ * for (const row of data) console.log(row.country, row.engagement.unique_opens);
2786
+ */
2787
+ byLocation(query, options) {
2788
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByLocation({
2789
+ client: this.client,
2790
+ query,
2791
+ headers,
2792
+ signal
2793
+ }));
2794
+ }
2795
+ /**
2796
+ * Breakdown by opening client (the application that opened the message).
2797
+ *
2798
+ * @example By client
2799
+ * const { data } = await bird.email.stats.byClient({
2800
+ * from: "2026-05-01",
2801
+ * to: "2026-05-31",
2802
+ * limit: 25,
2803
+ * });
2804
+ * for (const row of data) console.log(row.email_client, row.engagement.unique_opens);
2805
+ */
2806
+ byClient(query, options) {
2807
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByClient({
2808
+ client: this.client,
2809
+ query,
2810
+ headers,
2811
+ signal
2812
+ }));
2813
+ }
2814
+ /**
2815
+ * Breakdown by bounce code — which SMTP/enhanced codes drove bounces.
2816
+ *
2817
+ * @example By bounce code
2818
+ * const { data } = await bird.email.stats.byBounceCode({
2819
+ * from: "2026-05-01",
2820
+ * to: "2026-05-31",
2821
+ * sort: "bounced",
2822
+ * limit: 25,
2823
+ * });
2824
+ * for (const row of data) console.log(row.smtp_error_code, row.bounced);
2825
+ */
2826
+ byBounceCode(query, options) {
2827
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByBounceCode({
2828
+ client: this.client,
2829
+ query,
2830
+ headers,
2831
+ signal
2832
+ }));
2833
+ }
2834
+ /**
2835
+ * Breakdown by complaint type.
2836
+ *
2837
+ * @example By complaint type
2838
+ * const { data } = await bird.email.stats.byComplaintType({ from: "2026-05-01", to: "2026-05-31" });
2839
+ * for (const row of data) console.log(row.feedback_type, row.complained);
2840
+ */
2841
+ byComplaintType(query, options) {
2842
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByComplaintType({
2843
+ client: this.client,
2844
+ query,
2845
+ headers,
2846
+ signal
2847
+ }));
2848
+ }
2849
+ /**
2850
+ * Breakdown by broadcast.
2851
+ *
2852
+ * @example By broadcast
2853
+ * const { data } = await bird.email.stats.byBroadcast({
2854
+ * from: "2026-05-01",
2855
+ * to: "2026-05-31",
2856
+ * sort: "click_rate",
2857
+ * limit: 25,
2858
+ * });
2859
+ * for (const row of data) console.log(row.broadcast_id, row.engagement.click_rate);
2860
+ */
2861
+ byBroadcast(query, options) {
2862
+ return this.call("GET", options, ({ signal, headers }) => getEmailStatsByBroadcast({
2863
+ client: this.client,
2864
+ query,
2865
+ headers,
2866
+ signal
2867
+ }));
2868
+ }
2869
+ };
2870
+ //#endregion
2093
2871
  //#region src/resources/email.ts
2094
2872
  var EmailResource = class extends Resource {
2095
2873
  #defaults;
2874
+ /** Email statistics — `bird.email.stats.summary(...)`, `.daily(...)`, `.byTag(...)`, … */
2875
+ stats;
2096
2876
  constructor(core, client, defaults) {
2097
2877
  super(core, client);
2098
2878
  this.#defaults = defaults;
2879
+ this.stats = new EmailStatsResource(core, client);
2099
2880
  }
2100
2881
  /**
2101
2882
  * Send an email message. Resolves once the message is accepted for delivery
@@ -2737,7 +3518,8 @@ var SmsResource = class extends Resource {
2737
3518
  *
2738
3519
  * @example Send free text
2739
3520
  * const msg = await bird.sms.send({
2740
- * to: "+15551234567",
3521
+ * from: "MyBrand",
3522
+ * to: "+14155550100",
2741
3523
  * text: "Your verification code is 123456.",
2742
3524
  * category: "authentication",
2743
3525
  * });
@@ -2745,7 +3527,7 @@ var SmsResource = class extends Resource {
2745
3527
  *
2746
3528
  * @example Send by template
2747
3529
  * await bird.sms.send({
2748
- * to: "+15551234567",
3530
+ * to: "+14155550100",
2749
3531
  * template: { name: "bird_otp_verification", parameters: { code: "123456" } },
2750
3532
  * });
2751
3533
  */
@@ -3032,19 +3814,19 @@ var WebhooksResource = class {
3032
3814
  * @example One call verifies the signature and returns the typed event
3033
3815
  * // Pass the RAW request body; set the secret via new BirdClient({ webhooks: { secret } }).
3034
3816
  * const event = bird.webhooks.unwrap(rawBody, headers);
3035
- * console.log(event.type); // discriminated union narrow on event.type
3817
+ * console.log(event.type); // discriminated union: narrow on event.type
3036
3818
  *
3037
- * @example Verify and dispatch pass the raw request body, never the parsed JSON
3819
+ * @example Verify and dispatch: pass the raw request body, never the parsed JSON
3038
3820
  * // new BirdClient({ apiKey, webhooks: { secret } })
3039
3821
  * try {
3040
3822
  * const event = bird.webhooks.unwrap(rawBody, req.headers);
3041
3823
  * switch (event.type) {
3042
3824
  * case "email.delivered":
3043
- * markDelivered(event.email_id, event.recipient); // narrowed; fields are flat
3825
+ * markDelivered(event.data.email_id, event.data.recipient); // narrowed by event.type
3044
3826
  * break;
3045
3827
  * case "email.bounced":
3046
3828
  * case "email.complained":
3047
- * suppress(event.recipient);
3829
+ * suppress(event.data.recipient);
3048
3830
  * break;
3049
3831
  * default: // unknown future event types — an older SDK won't break on a new one
3050
3832
  * }
@@ -3153,9 +3935,9 @@ var BirdClient = class {
3153
3935
  this.#headers = {
3154
3936
  ...opts.defaultHeaders,
3155
3937
  Authorization: `Bearer ${opts.apiKey}`,
3156
- "User-Agent": `bird-sdk-js/0.9.3`,
3938
+ "User-Agent": `bird-sdk-js/0.10.1`,
3157
3939
  "Bird-Surface": "sdk-js",
3158
- "Bird-Version": "0.9.3"
3940
+ "Bird-Version": "0.10.1"
3159
3941
  };
3160
3942
  const caller = detectCaller();
3161
3943
  if (caller) this.#headers["Bird-Caller"] = caller;
@@ -3265,14 +4047,18 @@ const WebhookEventType = {
3265
4047
  RealtimeCacheChannels: "realtime.cache_channels",
3266
4048
  RealtimeChannelExistence: "realtime.channel_existence",
3267
4049
  RealtimeClientEvents: "realtime.client_events",
4050
+ RealtimeConnectionCount: "realtime.connection_count",
3268
4051
  RealtimePresence: "realtime.presence",
3269
- RealtimeSubscriptionCount: "realtime.subscription_count",
3270
4052
  SmsAccepted: "sms.accepted",
3271
4053
  SmsDelivered: "sms.delivered",
3272
4054
  SmsExpired: "sms.expired",
3273
4055
  SmsFailed: "sms.failed",
3274
4056
  SmsRejected: "sms.rejected",
3275
4057
  SmsSent: "sms.sent",
4058
+ SmsTfnVerificationApproved: "sms.tfn_verification.approved",
4059
+ SmsTfnVerificationInfoRequested: "sms.tfn_verification.info_requested",
4060
+ SmsTfnVerificationRejected: "sms.tfn_verification.rejected",
4061
+ SmsTfnVerificationSubmitted: "sms.tfn_verification.submitted",
3276
4062
  SmsUndelivered: "sms.undelivered",
3277
4063
  VoiceCallAnswered: "voice.call.answered",
3278
4064
  VoiceCallEnded: "voice.call.ended",
@@ -3281,6 +4067,7 @@ const WebhookEventType = {
3281
4067
  WhatsappDelivered: "whatsapp.delivered",
3282
4068
  WhatsappFailed: "whatsapp.failed",
3283
4069
  WhatsappRead: "whatsapp.read",
4070
+ WhatsappRejected: "whatsapp.rejected",
3284
4071
  WhatsappSent: "whatsapp.sent"
3285
4072
  };
3286
4073
  //#endregion