@sendly/node 1.0.7 → 1.1.0

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.d.ts CHANGED
@@ -47,8 +47,13 @@ interface SendMessageRequest {
47
47
  }
48
48
  /**
49
49
  * Message status values
50
+ * Note: "sending" was removed as it doesn't exist in the database
50
51
  */
51
- type MessageStatus = "queued" | "sending" | "sent" | "delivered" | "failed";
52
+ type MessageStatus = "queued" | "sent" | "delivered" | "failed";
53
+ /**
54
+ * How the message was sent
55
+ */
56
+ type SenderType = "number_pool" | "alphanumeric" | "sandbox";
52
57
  /**
53
58
  * A sent or received SMS message
54
59
  */
@@ -73,6 +78,10 @@ interface Message {
73
78
  * Current delivery status
74
79
  */
75
80
  status: MessageStatus;
81
+ /**
82
+ * Message direction
83
+ */
84
+ direction: "outbound" | "inbound";
76
85
  /**
77
86
  * Error message if status is "failed"
78
87
  */
@@ -89,6 +98,25 @@ interface Message {
89
98
  * Whether this message was sent in sandbox mode
90
99
  */
91
100
  isSandbox: boolean;
101
+ /**
102
+ * How the message was sent
103
+ * - "number_pool": Sent from toll-free number pool (US/CA)
104
+ * - "alphanumeric": Sent with alphanumeric sender ID (international)
105
+ * - "sandbox": Sent in sandbox/test mode
106
+ */
107
+ senderType?: SenderType;
108
+ /**
109
+ * Telnyx message ID for tracking
110
+ */
111
+ telnyxMessageId?: string | null;
112
+ /**
113
+ * Warning message (e.g., when "from" is ignored for domestic messages)
114
+ */
115
+ warning?: string;
116
+ /**
117
+ * Note about sender behavior (e.g., toll-free number pool explanation)
118
+ */
119
+ senderNote?: string;
92
120
  /**
93
121
  * ISO 8601 timestamp when the message was created
94
122
  */
@@ -478,6 +506,219 @@ declare const SUPPORTED_COUNTRIES: Record<PricingTier, string[]>;
478
506
  * All supported country codes
479
507
  */
480
508
  declare const ALL_SUPPORTED_COUNTRIES: string[];
509
+ /**
510
+ * Webhook event types
511
+ */
512
+ type WebhookEventType = "message.sent" | "message.delivered" | "message.failed" | "message.bounced" | "message.queued";
513
+ /**
514
+ * Circuit breaker state for webhook delivery
515
+ */
516
+ type CircuitState = "closed" | "open" | "half_open";
517
+ /**
518
+ * Webhook delivery status
519
+ */
520
+ type DeliveryStatus = "pending" | "delivered" | "failed" | "cancelled";
521
+ /**
522
+ * A configured webhook endpoint
523
+ */
524
+ interface Webhook {
525
+ /** Unique webhook identifier (whk_xxx) */
526
+ id: string;
527
+ /** HTTPS endpoint URL */
528
+ url: string;
529
+ /** Event types this webhook subscribes to */
530
+ events: WebhookEventType[];
531
+ /** Optional description */
532
+ description?: string;
533
+ /** Whether the webhook is active */
534
+ isActive: boolean;
535
+ /** Number of consecutive failures */
536
+ failureCount: number;
537
+ /** Last failure timestamp (ISO 8601) */
538
+ lastFailureAt?: string | null;
539
+ /** Circuit breaker state */
540
+ circuitState: CircuitState;
541
+ /** When circuit was opened (ISO 8601) */
542
+ circuitOpenedAt?: string | null;
543
+ /** API version for payloads */
544
+ apiVersion: string;
545
+ /** Custom metadata */
546
+ metadata: Record<string, unknown>;
547
+ /** When webhook was created (ISO 8601) */
548
+ createdAt: string;
549
+ /** When webhook was last updated (ISO 8601) */
550
+ updatedAt: string;
551
+ /** Total delivery attempts */
552
+ totalDeliveries: number;
553
+ /** Successful deliveries */
554
+ successfulDeliveries: number;
555
+ /** Success rate (0-100) */
556
+ successRate: number;
557
+ /** Last successful delivery (ISO 8601) */
558
+ lastDeliveryAt?: string | null;
559
+ }
560
+ /**
561
+ * Response when creating a webhook (includes secret once)
562
+ */
563
+ interface WebhookCreatedResponse extends Webhook {
564
+ /** Webhook signing secret - only shown once at creation */
565
+ secret: string;
566
+ }
567
+ /**
568
+ * Options for creating a webhook
569
+ */
570
+ interface CreateWebhookOptions {
571
+ /** HTTPS endpoint URL */
572
+ url: string;
573
+ /** Event types to subscribe to */
574
+ events: WebhookEventType[];
575
+ /** Optional description */
576
+ description?: string;
577
+ /** Custom metadata */
578
+ metadata?: Record<string, unknown>;
579
+ }
580
+ /**
581
+ * Options for updating a webhook
582
+ */
583
+ interface UpdateWebhookOptions {
584
+ /** New URL */
585
+ url?: string;
586
+ /** New event subscriptions */
587
+ events?: WebhookEventType[];
588
+ /** New description */
589
+ description?: string;
590
+ /** Enable/disable webhook */
591
+ isActive?: boolean;
592
+ /** Custom metadata */
593
+ metadata?: Record<string, unknown>;
594
+ }
595
+ /**
596
+ * A webhook delivery attempt
597
+ */
598
+ interface WebhookDelivery {
599
+ /** Unique delivery identifier (del_xxx) */
600
+ id: string;
601
+ /** Webhook ID this delivery belongs to */
602
+ webhookId: string;
603
+ /** Event ID for idempotency */
604
+ eventId: string;
605
+ /** Event type */
606
+ eventType: WebhookEventType;
607
+ /** Attempt number (1-6) */
608
+ attemptNumber: number;
609
+ /** Maximum attempts allowed */
610
+ maxAttempts: number;
611
+ /** Delivery status */
612
+ status: DeliveryStatus;
613
+ /** HTTP response status code */
614
+ responseStatusCode?: number;
615
+ /** Response time in milliseconds */
616
+ responseTimeMs?: number;
617
+ /** Error message if failed */
618
+ errorMessage?: string;
619
+ /** Error code if failed */
620
+ errorCode?: string;
621
+ /** Next retry time (ISO 8601) */
622
+ nextRetryAt?: string;
623
+ /** When delivery was created (ISO 8601) */
624
+ createdAt: string;
625
+ /** When delivery succeeded (ISO 8601) */
626
+ deliveredAt?: string;
627
+ }
628
+ /**
629
+ * Response from testing a webhook
630
+ */
631
+ interface WebhookTestResult {
632
+ /** Whether test was successful */
633
+ success: boolean;
634
+ /** HTTP status code from endpoint */
635
+ statusCode?: number;
636
+ /** Response time in milliseconds */
637
+ responseTimeMs?: number;
638
+ /** Error message if failed */
639
+ error?: string;
640
+ }
641
+ /**
642
+ * Response from rotating webhook secret
643
+ */
644
+ interface WebhookSecretRotation {
645
+ /** The webhook */
646
+ webhook: Webhook;
647
+ /** New signing secret */
648
+ newSecret: string;
649
+ /** When old secret expires (ISO 8601) */
650
+ oldSecretExpiresAt: string;
651
+ /** Message about grace period */
652
+ message: string;
653
+ }
654
+ /**
655
+ * Account information
656
+ */
657
+ interface Account {
658
+ /** User ID */
659
+ id: string;
660
+ /** Email address */
661
+ email: string;
662
+ /** Display name */
663
+ name?: string;
664
+ /** Account creation date (ISO 8601) */
665
+ createdAt: string;
666
+ }
667
+ /**
668
+ * Credit balance information
669
+ */
670
+ interface Credits {
671
+ /** Available credit balance */
672
+ balance: number;
673
+ /** Credits reserved for scheduled messages */
674
+ reservedBalance: number;
675
+ /** Total usable credits (balance - reserved) */
676
+ availableBalance: number;
677
+ }
678
+ /**
679
+ * A credit transaction record
680
+ */
681
+ interface CreditTransaction {
682
+ /** Transaction ID */
683
+ id: string;
684
+ /** Transaction type */
685
+ type: "purchase" | "usage" | "refund" | "adjustment" | "bonus";
686
+ /** Amount (positive for credits in, negative for credits out) */
687
+ amount: number;
688
+ /** Balance after transaction */
689
+ balanceAfter: number;
690
+ /** Transaction description */
691
+ description: string;
692
+ /** Related message ID (for usage transactions) */
693
+ messageId?: string;
694
+ /** When transaction occurred (ISO 8601) */
695
+ createdAt: string;
696
+ }
697
+ /**
698
+ * An API key
699
+ */
700
+ interface ApiKey {
701
+ /** Key ID */
702
+ id: string;
703
+ /** Key name/label */
704
+ name: string;
705
+ /** Key type */
706
+ type: "test" | "live";
707
+ /** Key prefix (for identification) */
708
+ prefix: string;
709
+ /** Last 4 characters of key */
710
+ lastFour: string;
711
+ /** Permissions granted */
712
+ permissions: string[];
713
+ /** When key was created (ISO 8601) */
714
+ createdAt: string;
715
+ /** When key was last used (ISO 8601) */
716
+ lastUsedAt?: string | null;
717
+ /** When key expires (ISO 8601) */
718
+ expiresAt?: string | null;
719
+ /** Whether key is revoked */
720
+ isRevoked: boolean;
721
+ }
481
722
  /**
482
723
  * Test phone numbers for sandbox mode
483
724
  */
@@ -805,6 +1046,331 @@ declare class MessagesResource {
805
1046
  listBatches(options?: ListBatchesOptions): Promise<BatchListResponse>;
806
1047
  }
807
1048
 
1049
+ /**
1050
+ * Webhooks Resource
1051
+ * @packageDocumentation
1052
+ */
1053
+
1054
+ /**
1055
+ * Webhooks API resource
1056
+ *
1057
+ * Manage webhook endpoints for receiving real-time message status updates.
1058
+ *
1059
+ * @example
1060
+ * ```typescript
1061
+ * // Create a webhook
1062
+ * const webhook = await sendly.webhooks.create({
1063
+ * url: 'https://example.com/webhooks/sendly',
1064
+ * events: ['message.delivered', 'message.failed']
1065
+ * });
1066
+ *
1067
+ * // IMPORTANT: Save the secret - it's only shown once!
1068
+ * console.log('Secret:', webhook.secret);
1069
+ *
1070
+ * // List webhooks
1071
+ * const webhooks = await sendly.webhooks.list();
1072
+ *
1073
+ * // Test a webhook
1074
+ * const result = await sendly.webhooks.test(webhook.id);
1075
+ * ```
1076
+ */
1077
+ declare class WebhooksResource {
1078
+ private readonly http;
1079
+ constructor(http: HttpClient);
1080
+ /**
1081
+ * Create a new webhook endpoint
1082
+ *
1083
+ * @param options - Webhook configuration
1084
+ * @returns The created webhook with signing secret (shown only once!)
1085
+ *
1086
+ * @example
1087
+ * ```typescript
1088
+ * const webhook = await sendly.webhooks.create({
1089
+ * url: 'https://example.com/webhooks/sendly',
1090
+ * events: ['message.delivered', 'message.failed'],
1091
+ * description: 'Production webhook'
1092
+ * });
1093
+ *
1094
+ * // IMPORTANT: Save this secret securely - it's only shown once!
1095
+ * console.log('Webhook secret:', webhook.secret);
1096
+ * ```
1097
+ *
1098
+ * @throws {ValidationError} If the URL is invalid or events are empty
1099
+ * @throws {AuthenticationError} If the API key is invalid
1100
+ */
1101
+ create(options: CreateWebhookOptions): Promise<WebhookCreatedResponse>;
1102
+ /**
1103
+ * List all webhooks
1104
+ *
1105
+ * @returns Array of webhook configurations
1106
+ *
1107
+ * @example
1108
+ * ```typescript
1109
+ * const webhooks = await sendly.webhooks.list();
1110
+ *
1111
+ * for (const webhook of webhooks) {
1112
+ * console.log(`${webhook.id}: ${webhook.url} (${webhook.isActive ? 'active' : 'inactive'})`);
1113
+ * }
1114
+ * ```
1115
+ */
1116
+ list(): Promise<Webhook[]>;
1117
+ /**
1118
+ * Get a specific webhook by ID
1119
+ *
1120
+ * @param id - Webhook ID (whk_xxx)
1121
+ * @returns The webhook details
1122
+ *
1123
+ * @example
1124
+ * ```typescript
1125
+ * const webhook = await sendly.webhooks.get('whk_xxx');
1126
+ * console.log(`Success rate: ${webhook.successRate}%`);
1127
+ * ```
1128
+ *
1129
+ * @throws {NotFoundError} If the webhook doesn't exist
1130
+ */
1131
+ get(id: string): Promise<Webhook>;
1132
+ /**
1133
+ * Update a webhook configuration
1134
+ *
1135
+ * @param id - Webhook ID
1136
+ * @param options - Fields to update
1137
+ * @returns The updated webhook
1138
+ *
1139
+ * @example
1140
+ * ```typescript
1141
+ * // Update URL
1142
+ * await sendly.webhooks.update('whk_xxx', {
1143
+ * url: 'https://new-endpoint.example.com/webhooks'
1144
+ * });
1145
+ *
1146
+ * // Disable webhook
1147
+ * await sendly.webhooks.update('whk_xxx', { isActive: false });
1148
+ *
1149
+ * // Change event subscriptions
1150
+ * await sendly.webhooks.update('whk_xxx', {
1151
+ * events: ['message.delivered']
1152
+ * });
1153
+ * ```
1154
+ */
1155
+ update(id: string, options: UpdateWebhookOptions): Promise<Webhook>;
1156
+ /**
1157
+ * Delete a webhook
1158
+ *
1159
+ * @param id - Webhook ID
1160
+ *
1161
+ * @example
1162
+ * ```typescript
1163
+ * await sendly.webhooks.delete('whk_xxx');
1164
+ * ```
1165
+ *
1166
+ * @throws {NotFoundError} If the webhook doesn't exist
1167
+ */
1168
+ delete(id: string): Promise<void>;
1169
+ /**
1170
+ * Send a test event to a webhook endpoint
1171
+ *
1172
+ * @param id - Webhook ID
1173
+ * @returns Test result with response details
1174
+ *
1175
+ * @example
1176
+ * ```typescript
1177
+ * const result = await sendly.webhooks.test('whk_xxx');
1178
+ *
1179
+ * if (result.success) {
1180
+ * console.log(`Test passed! Response time: ${result.responseTimeMs}ms`);
1181
+ * } else {
1182
+ * console.log(`Test failed: ${result.error}`);
1183
+ * }
1184
+ * ```
1185
+ */
1186
+ test(id: string): Promise<WebhookTestResult>;
1187
+ /**
1188
+ * Rotate the webhook signing secret
1189
+ *
1190
+ * The old secret remains valid for 24 hours to allow for graceful migration.
1191
+ *
1192
+ * @param id - Webhook ID
1193
+ * @returns New secret and expiration info
1194
+ *
1195
+ * @example
1196
+ * ```typescript
1197
+ * const rotation = await sendly.webhooks.rotateSecret('whk_xxx');
1198
+ *
1199
+ * // Update your webhook handler with the new secret
1200
+ * console.log('New secret:', rotation.newSecret);
1201
+ * console.log('Old secret expires:', rotation.oldSecretExpiresAt);
1202
+ * ```
1203
+ */
1204
+ rotateSecret(id: string): Promise<WebhookSecretRotation>;
1205
+ /**
1206
+ * Get delivery history for a webhook
1207
+ *
1208
+ * @param id - Webhook ID
1209
+ * @returns Array of delivery attempts
1210
+ *
1211
+ * @example
1212
+ * ```typescript
1213
+ * const deliveries = await sendly.webhooks.getDeliveries('whk_xxx');
1214
+ *
1215
+ * for (const delivery of deliveries) {
1216
+ * console.log(`${delivery.eventType}: ${delivery.status} (${delivery.responseTimeMs}ms)`);
1217
+ * }
1218
+ * ```
1219
+ */
1220
+ getDeliveries(id: string): Promise<WebhookDelivery[]>;
1221
+ /**
1222
+ * Retry a failed delivery
1223
+ *
1224
+ * @param webhookId - Webhook ID
1225
+ * @param deliveryId - Delivery ID
1226
+ *
1227
+ * @example
1228
+ * ```typescript
1229
+ * await sendly.webhooks.retryDelivery('whk_xxx', 'del_yyy');
1230
+ * ```
1231
+ */
1232
+ retryDelivery(webhookId: string, deliveryId: string): Promise<void>;
1233
+ /**
1234
+ * List available event types
1235
+ *
1236
+ * @returns Array of event type strings
1237
+ *
1238
+ * @example
1239
+ * ```typescript
1240
+ * const eventTypes = await sendly.webhooks.listEventTypes();
1241
+ * console.log('Available events:', eventTypes);
1242
+ * // ['message.sent', 'message.delivered', 'message.failed', 'message.bounced']
1243
+ * ```
1244
+ */
1245
+ listEventTypes(): Promise<WebhookEventType[]>;
1246
+ }
1247
+
1248
+ /**
1249
+ * Account Resource
1250
+ * @packageDocumentation
1251
+ */
1252
+
1253
+ /**
1254
+ * Account API resource
1255
+ *
1256
+ * Access account information, credit balance, and API keys.
1257
+ *
1258
+ * @example
1259
+ * ```typescript
1260
+ * // Get credit balance
1261
+ * const credits = await sendly.account.getCredits();
1262
+ * console.log(`Available: ${credits.availableBalance} credits`);
1263
+ *
1264
+ * // Get transaction history
1265
+ * const transactions = await sendly.account.getCreditTransactions();
1266
+ *
1267
+ * // List API keys
1268
+ * const keys = await sendly.account.listApiKeys();
1269
+ * ```
1270
+ */
1271
+ declare class AccountResource {
1272
+ private readonly http;
1273
+ constructor(http: HttpClient);
1274
+ /**
1275
+ * Get account information
1276
+ *
1277
+ * @returns Account details
1278
+ *
1279
+ * @example
1280
+ * ```typescript
1281
+ * const account = await sendly.account.get();
1282
+ * console.log(`Account: ${account.email}`);
1283
+ * ```
1284
+ */
1285
+ get(): Promise<Account>;
1286
+ /**
1287
+ * Get credit balance
1288
+ *
1289
+ * @returns Current credit balance and reserved credits
1290
+ *
1291
+ * @example
1292
+ * ```typescript
1293
+ * const credits = await sendly.account.getCredits();
1294
+ *
1295
+ * console.log(`Total balance: ${credits.balance}`);
1296
+ * console.log(`Reserved (scheduled): ${credits.reservedBalance}`);
1297
+ * console.log(`Available to use: ${credits.availableBalance}`);
1298
+ * ```
1299
+ */
1300
+ getCredits(): Promise<Credits>;
1301
+ /**
1302
+ * Get credit transaction history
1303
+ *
1304
+ * @param options - Pagination options
1305
+ * @returns Array of credit transactions
1306
+ *
1307
+ * @example
1308
+ * ```typescript
1309
+ * const transactions = await sendly.account.getCreditTransactions();
1310
+ *
1311
+ * for (const tx of transactions) {
1312
+ * const sign = tx.amount > 0 ? '+' : '';
1313
+ * console.log(`${tx.type}: ${sign}${tx.amount} credits - ${tx.description}`);
1314
+ * }
1315
+ * ```
1316
+ */
1317
+ getCreditTransactions(options?: {
1318
+ limit?: number;
1319
+ offset?: number;
1320
+ }): Promise<CreditTransaction[]>;
1321
+ /**
1322
+ * List API keys for the account
1323
+ *
1324
+ * Note: This returns key metadata, not the actual secret keys.
1325
+ *
1326
+ * @returns Array of API keys
1327
+ *
1328
+ * @example
1329
+ * ```typescript
1330
+ * const keys = await sendly.account.listApiKeys();
1331
+ *
1332
+ * for (const key of keys) {
1333
+ * console.log(`${key.name}: ${key.prefix}...${key.lastFour} (${key.type})`);
1334
+ * }
1335
+ * ```
1336
+ */
1337
+ listApiKeys(): Promise<ApiKey[]>;
1338
+ /**
1339
+ * Get a specific API key by ID
1340
+ *
1341
+ * @param id - API key ID
1342
+ * @returns API key details
1343
+ *
1344
+ * @example
1345
+ * ```typescript
1346
+ * const key = await sendly.account.getApiKey('key_xxx');
1347
+ * console.log(`Last used: ${key.lastUsedAt}`);
1348
+ * ```
1349
+ */
1350
+ getApiKey(id: string): Promise<ApiKey>;
1351
+ /**
1352
+ * Get usage statistics for an API key
1353
+ *
1354
+ * @param id - API key ID
1355
+ * @returns Usage statistics
1356
+ *
1357
+ * @example
1358
+ * ```typescript
1359
+ * const usage = await sendly.account.getApiKeyUsage('key_xxx');
1360
+ * console.log(`Messages sent: ${usage.messagesSent}`);
1361
+ * ```
1362
+ */
1363
+ getApiKeyUsage(id: string): Promise<{
1364
+ keyId: string;
1365
+ messagesSent: number;
1366
+ messagesDelivered: number;
1367
+ messagesFailed: number;
1368
+ creditsUsed: number;
1369
+ periodStart: string;
1370
+ periodEnd: string;
1371
+ }>;
1372
+ }
1373
+
808
1374
  /**
809
1375
  * Sendly Client
810
1376
  * @packageDocumentation
@@ -858,6 +1424,41 @@ declare class Sendly {
858
1424
  * ```
859
1425
  */
860
1426
  readonly messages: MessagesResource;
1427
+ /**
1428
+ * Webhooks API resource
1429
+ *
1430
+ * @example
1431
+ * ```typescript
1432
+ * // Create a webhook
1433
+ * const webhook = await sendly.webhooks.create({
1434
+ * url: 'https://example.com/webhooks',
1435
+ * events: ['message.delivered', 'message.failed']
1436
+ * });
1437
+ *
1438
+ * // List webhooks
1439
+ * const webhooks = await sendly.webhooks.list();
1440
+ *
1441
+ * // Test a webhook
1442
+ * await sendly.webhooks.test('whk_xxx');
1443
+ * ```
1444
+ */
1445
+ readonly webhooks: WebhooksResource;
1446
+ /**
1447
+ * Account API resource
1448
+ *
1449
+ * @example
1450
+ * ```typescript
1451
+ * // Get credit balance
1452
+ * const credits = await sendly.account.getCredits();
1453
+ *
1454
+ * // Get transaction history
1455
+ * const transactions = await sendly.account.getCreditTransactions();
1456
+ *
1457
+ * // List API keys
1458
+ * const keys = await sendly.account.listApiKeys();
1459
+ * ```
1460
+ */
1461
+ readonly account: AccountResource;
861
1462
  private readonly http;
862
1463
  private readonly config;
863
1464
  /**
@@ -1020,53 +1621,86 @@ declare function calculateSegments(text: string): number;
1020
1621
  * Provides signature verification and event parsing
1021
1622
  * @packageDocumentation
1022
1623
  */
1023
- /**
1024
- * Webhook event types
1025
- */
1026
- type WebhookEventType = "message.queued" | "message.sent" | "message.delivered" | "message.failed" | "message.undelivered";
1624
+
1027
1625
  /**
1028
1626
  * Message status in webhook events
1029
1627
  */
1030
- type WebhookMessageStatus = "queued" | "sent" | "delivered" | "failed" | "undelivered";
1628
+ type WebhookMessageStatus = "queued" | "sent" | "delivered" | "failed";
1031
1629
  /**
1032
- * Webhook message data payload
1630
+ * Message object within webhook payload
1631
+ * Matches the structure sent by Sendly servers
1033
1632
  */
1034
- interface WebhookMessageData {
1035
- /** Unique message identifier */
1036
- messageId: string;
1037
- /** Current message status */
1038
- status: WebhookMessageStatus;
1039
- /** Destination phone number */
1633
+ interface WebhookMessageObject {
1634
+ /** Message ID (msg_xxx) */
1635
+ id: string;
1636
+ /** Recipient phone number (E.164 format) */
1040
1637
  to: string;
1041
- /** Sender ID or phone number */
1638
+ /** Sender phone number or ID */
1042
1639
  from: string;
1043
- /** Error message if failed */
1044
- error?: string;
1045
- /** Error code if failed */
1046
- errorCode?: string;
1047
- /** ISO 8601 timestamp when delivered */
1048
- deliveredAt?: string;
1049
- /** ISO 8601 timestamp when failed */
1050
- failedAt?: string;
1640
+ /** Message text content */
1641
+ text: string;
1642
+ /** Current message status */
1643
+ status: WebhookMessageStatus;
1644
+ /** Message direction */
1645
+ direction: "outbound" | "inbound";
1051
1646
  /** Number of SMS segments */
1052
1647
  segments: number;
1053
- /** Credits charged */
1054
- creditsUsed: number;
1648
+ /** Credits charged for this message */
1649
+ credits_used: number;
1650
+ /** Unix timestamp when message was created */
1651
+ created_at: number;
1652
+ /** Unix timestamp when message was delivered (if applicable) */
1653
+ delivered_at?: number;
1654
+ /** Error message if status is 'failed' */
1655
+ error?: string;
1656
+ /** Custom metadata attached to the message */
1657
+ metadata?: Record<string, unknown>;
1055
1658
  }
1056
1659
  /**
1057
- * Webhook event structure
1660
+ * Webhook event payload from Sendly
1661
+ * This is the exact structure sent to your webhook endpoints
1662
+ *
1663
+ * @example
1664
+ * ```json
1665
+ * {
1666
+ * "id": "evt_abc123",
1667
+ * "type": "message.delivered",
1668
+ * "api_version": "2024-01",
1669
+ * "created": 1702000000,
1670
+ * "livemode": true,
1671
+ * "data": {
1672
+ * "object": {
1673
+ * "id": "msg_xyz789",
1674
+ * "to": "+15551234567",
1675
+ * "from": "+15559876543",
1676
+ * "text": "Hello!",
1677
+ * "status": "delivered",
1678
+ * "direction": "outbound",
1679
+ * "segments": 1,
1680
+ * "credits_used": 1,
1681
+ * "created_at": 1702000000,
1682
+ * "delivered_at": 1702000005
1683
+ * }
1684
+ * }
1685
+ * }
1686
+ * ```
1058
1687
  */
1059
1688
  interface WebhookEvent {
1060
- /** Unique event identifier */
1689
+ /** Unique event identifier (evt_xxx) for idempotency */
1061
1690
  id: string;
1062
1691
  /** Event type */
1063
1692
  type: WebhookEventType;
1064
- /** Event data payload */
1065
- data: WebhookMessageData;
1066
- /** ISO 8601 timestamp when event was created */
1067
- createdAt: string;
1068
1693
  /** API version that generated this event */
1069
- apiVersion: string;
1694
+ api_version: string;
1695
+ /** Unix timestamp (seconds) when event was created */
1696
+ created: number;
1697
+ /** Whether this event is from live mode (true) or sandbox (false) */
1698
+ livemode: boolean;
1699
+ /** Event data containing the message object */
1700
+ data: {
1701
+ /** The message object that triggered this event */
1702
+ object: WebhookMessageObject;
1703
+ };
1070
1704
  }
1071
1705
  /**
1072
1706
  * Error thrown when webhook signature verification fails
@@ -1077,33 +1711,50 @@ declare class WebhookSignatureError extends Error {
1077
1711
  /**
1078
1712
  * Verify a webhook signature from Sendly
1079
1713
  *
1080
- * @param payload - Raw request body as string
1714
+ * Sendly signs webhooks using HMAC-SHA256. The signature format is:
1715
+ * `sha256=<hex_digest>`
1716
+ *
1717
+ * The signed payload is: `<timestamp>.<json_body>`
1718
+ *
1719
+ * @param payload - Raw request body as string (JSON)
1081
1720
  * @param signature - X-Sendly-Signature header value
1082
1721
  * @param secret - Your webhook secret from dashboard
1722
+ * @param timestamp - X-Sendly-Timestamp header value (optional, for enhanced verification)
1723
+ * @param toleranceSeconds - Maximum age of webhook in seconds (default: 300 = 5 minutes)
1083
1724
  * @returns true if signature is valid
1084
1725
  *
1085
1726
  * @example
1086
1727
  * ```typescript
1087
1728
  * import { verifyWebhookSignature } from '@sendly/node';
1088
1729
  *
1730
+ * // Basic verification
1089
1731
  * const isValid = verifyWebhookSignature(
1090
1732
  * req.body, // raw body string
1091
1733
  * req.headers['x-sendly-signature'],
1092
1734
  * process.env.WEBHOOK_SECRET
1093
1735
  * );
1094
1736
  *
1737
+ * // With timestamp verification (recommended)
1738
+ * const isValid = verifyWebhookSignature(
1739
+ * req.body,
1740
+ * req.headers['x-sendly-signature'],
1741
+ * process.env.WEBHOOK_SECRET,
1742
+ * req.headers['x-sendly-timestamp']
1743
+ * );
1744
+ *
1095
1745
  * if (!isValid) {
1096
1746
  * return res.status(401).send('Invalid signature');
1097
1747
  * }
1098
1748
  * ```
1099
1749
  */
1100
- declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
1750
+ declare function verifyWebhookSignature(payload: string, signature: string, secret: string, timestamp?: string, toleranceSeconds?: number): boolean;
1101
1751
  /**
1102
1752
  * Parse and verify a webhook event
1103
1753
  *
1104
1754
  * @param payload - Raw request body as string
1105
1755
  * @param signature - X-Sendly-Signature header value
1106
1756
  * @param secret - Your webhook secret from dashboard
1757
+ * @param timestamp - X-Sendly-Timestamp header value (optional)
1107
1758
  * @returns Parsed webhook event
1108
1759
  * @throws {WebhookSignatureError} If signature is invalid
1109
1760
  *
@@ -1115,15 +1766,16 @@ declare function verifyWebhookSignature(payload: string, signature: string, secr
1115
1766
  * const event = parseWebhookEvent(
1116
1767
  * req.body,
1117
1768
  * req.headers['x-sendly-signature'],
1118
- * process.env.WEBHOOK_SECRET
1769
+ * process.env.WEBHOOK_SECRET,
1770
+ * req.headers['x-sendly-timestamp']
1119
1771
  * );
1120
1772
  *
1121
1773
  * switch (event.type) {
1122
1774
  * case 'message.delivered':
1123
- * console.log(`Message ${event.data.messageId} delivered!`);
1775
+ * console.log(`Message ${event.data.object.id} delivered!`);
1124
1776
  * break;
1125
1777
  * case 'message.failed':
1126
- * console.log(`Message failed: ${event.data.error}`);
1778
+ * console.log(`Message failed: ${event.data.object.error}`);
1127
1779
  * break;
1128
1780
  * }
1129
1781
  * } catch (err) {
@@ -1134,7 +1786,7 @@ declare function verifyWebhookSignature(payload: string, signature: string, secr
1134
1786
  * }
1135
1787
  * ```
1136
1788
  */
1137
- declare function parseWebhookEvent(payload: string, signature: string, secret: string): WebhookEvent;
1789
+ declare function parseWebhookEvent(payload: string, signature: string, secret: string, timestamp?: string): WebhookEvent;
1138
1790
  /**
1139
1791
  * Generate a webhook signature for testing purposes
1140
1792
  *
@@ -1147,15 +1799,31 @@ declare function parseWebhookEvent(payload: string, signature: string, secret: s
1147
1799
  * import { generateWebhookSignature } from '@sendly/node';
1148
1800
  *
1149
1801
  * // For testing your webhook handler
1802
+ * const timestamp = Math.floor(Date.now() / 1000);
1150
1803
  * const testPayload = JSON.stringify({
1151
1804
  * id: 'evt_test',
1152
1805
  * type: 'message.delivered',
1153
- * data: { messageId: 'msg_123', status: 'delivered' },
1154
- * createdAt: new Date().toISOString(),
1155
- * apiVersion: '2025-01-01'
1806
+ * api_version: '2024-01',
1807
+ * created: timestamp,
1808
+ * livemode: false,
1809
+ * data: {
1810
+ * object: {
1811
+ * id: 'msg_123',
1812
+ * to: '+15551234567',
1813
+ * from: '+15559876543',
1814
+ * text: 'Test message',
1815
+ * status: 'delivered',
1816
+ * direction: 'outbound',
1817
+ * segments: 1,
1818
+ * credits_used: 1,
1819
+ * created_at: timestamp,
1820
+ * delivered_at: timestamp
1821
+ * }
1822
+ * }
1156
1823
  * });
1157
1824
  *
1158
- * const signature = generateWebhookSignature(testPayload, 'test_secret');
1825
+ * const signedPayload = `${timestamp}.${testPayload}`;
1826
+ * const signature = generateWebhookSignature(signedPayload, 'test_secret');
1159
1827
  * ```
1160
1828
  */
1161
1829
  declare function generateWebhookSignature(payload: string, secret: string): string;
@@ -1168,11 +1836,25 @@ declare function generateWebhookSignature(payload: string, secret: string): stri
1168
1836
  *
1169
1837
  * const webhooks = new Webhooks('your_webhook_secret');
1170
1838
  *
1171
- * // Verify signature
1172
- * const isValid = webhooks.verify(payload, signature);
1839
+ * // In your Express/Fastify/etc handler:
1840
+ * app.post('/webhooks/sendly', (req, res) => {
1841
+ * try {
1842
+ * const event = webhooks.parse(
1843
+ * req.body,
1844
+ * req.headers['x-sendly-signature'],
1845
+ * req.headers['x-sendly-timestamp']
1846
+ * );
1847
+ *
1848
+ * // Handle the event
1849
+ * if (event.type === 'message.delivered') {
1850
+ * console.log(`Message ${event.data.object.id} delivered!`);
1851
+ * }
1173
1852
  *
1174
- * // Parse event
1175
- * const event = webhooks.parse(payload, signature);
1853
+ * res.status(200).send('OK');
1854
+ * } catch (err) {
1855
+ * res.status(401).send('Invalid signature');
1856
+ * }
1857
+ * });
1176
1858
  * ```
1177
1859
  */
1178
1860
  declare class Webhooks {
@@ -1186,19 +1868,45 @@ declare class Webhooks {
1186
1868
  * Verify a webhook signature
1187
1869
  * @param payload - Raw request body
1188
1870
  * @param signature - X-Sendly-Signature header
1871
+ * @param timestamp - X-Sendly-Timestamp header (optional)
1189
1872
  */
1190
- verify(payload: string, signature: string): boolean;
1873
+ verify(payload: string, signature: string, timestamp?: string): boolean;
1191
1874
  /**
1192
1875
  * Parse and verify a webhook event
1193
1876
  * @param payload - Raw request body
1194
1877
  * @param signature - X-Sendly-Signature header
1878
+ * @param timestamp - X-Sendly-Timestamp header (optional)
1195
1879
  */
1196
- parse(payload: string, signature: string): WebhookEvent;
1880
+ parse(payload: string, signature: string, timestamp?: string): WebhookEvent;
1197
1881
  /**
1198
1882
  * Generate a signature for testing
1199
- * @param payload - Payload to sign
1883
+ * @param payload - Payload to sign (should include timestamp prefix if using timestamps)
1200
1884
  */
1201
1885
  sign(payload: string): string;
1886
+ /**
1887
+ * Verify a webhook signature (static method for backwards compatibility)
1888
+ * @param payload - Raw request body
1889
+ * @param signature - X-Sendly-Signature header
1890
+ * @param secret - Your webhook secret
1891
+ */
1892
+ static verifySignature(payload: string, signature: string, secret: string): boolean;
1893
+ /**
1894
+ * Parse and verify a webhook event (static method for backwards compatibility)
1895
+ * @param payload - Raw request body
1896
+ * @param signature - X-Sendly-Signature header
1897
+ * @param secret - Your webhook secret
1898
+ */
1899
+ static parseEvent(payload: string, signature: string, secret: string): WebhookEvent;
1900
+ /**
1901
+ * Generate a webhook signature (static method for backwards compatibility)
1902
+ * @param payload - Payload to sign
1903
+ * @param secret - Secret to use for signing
1904
+ */
1905
+ static generateSignature(payload: string, secret: string): string;
1202
1906
  }
1907
+ /**
1908
+ * @deprecated Use WebhookMessageObject instead
1909
+ */
1910
+ type WebhookMessageData = WebhookMessageObject;
1203
1911
 
1204
- export { ALL_SUPPORTED_COUNTRIES, type ApiErrorResponse, AuthenticationError, CREDITS_PER_SMS, InsufficientCreditsError, type ListMessagesOptions, type Message, type MessageListResponse, type MessageStatus, NetworkError, NotFoundError, type PricingTier, RateLimitError, type RateLimitInfo, SANDBOX_TEST_NUMBERS, SUPPORTED_COUNTRIES, type SendMessageRequest, Sendly, type SendlyConfig, SendlyError, type SendlyErrorCode, TimeoutError, ValidationError, type WebhookEvent, type WebhookEventType, type WebhookMessageData, type WebhookMessageStatus, WebhookSignatureError, Webhooks, calculateSegments, Sendly as default, generateWebhookSignature, getCountryFromPhone, isCountrySupported, parseWebhookEvent, validateMessageText, validatePhoneNumber, validateSenderId, verifyWebhookSignature };
1912
+ export { ALL_SUPPORTED_COUNTRIES, type Account, type ApiErrorResponse, type ApiKey, AuthenticationError, type BatchListResponse, type BatchMessageItem, type BatchMessageRequest, type BatchMessageResponse, type BatchMessageResult, type BatchStatus, CREDITS_PER_SMS, type CancelledMessageResponse, type CircuitState, type CreateWebhookOptions, type CreditTransaction, type Credits, type DeliveryStatus, InsufficientCreditsError, type ListBatchesOptions, type ListMessagesOptions, type ListScheduledMessagesOptions, type Message, type MessageListResponse, type MessageStatus, NetworkError, NotFoundError, type PricingTier, RateLimitError, type RateLimitInfo, SANDBOX_TEST_NUMBERS, SUPPORTED_COUNTRIES, type ScheduleMessageRequest, type ScheduledMessage, type ScheduledMessageListResponse, type ScheduledMessageStatus, type SendMessageRequest, type SenderType, Sendly, type SendlyConfig, SendlyError, type SendlyErrorCode, TimeoutError, type UpdateWebhookOptions, ValidationError, type Webhook, type WebhookCreatedResponse, type WebhookDelivery, type WebhookEvent, type WebhookEventType, type WebhookMessageData, type WebhookMessageStatus, type WebhookSecretRotation, WebhookSignatureError, type WebhookTestResult, Webhooks, calculateSegments, Sendly as default, generateWebhookSignature, getCountryFromPhone, isCountrySupported, parseWebhookEvent, validateMessageText, validatePhoneNumber, validateSenderId, verifyWebhookSignature };