@sendly/node 1.0.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/README.md +329 -0
- package/dist/index.d.mts +1204 -0
- package/dist/index.d.ts +1204 -0
- package/dist/index.js +1181 -0
- package/dist/index.mjs +1121 -0
- package/package.json +54 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,1204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sendly Node.js SDK Types
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for the Sendly client
|
|
7
|
+
*/
|
|
8
|
+
interface SendlyConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Your Sendly API key (sk_test_v1_xxx or sk_live_v1_xxx)
|
|
11
|
+
*/
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/**
|
|
14
|
+
* Base URL for the Sendly API
|
|
15
|
+
* @default "https://sendly.live/api"
|
|
16
|
+
*/
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Request timeout in milliseconds
|
|
20
|
+
* @default 30000
|
|
21
|
+
*/
|
|
22
|
+
timeout?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Maximum number of retry attempts for failed requests
|
|
25
|
+
* @default 3
|
|
26
|
+
*/
|
|
27
|
+
maxRetries?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Request payload for sending an SMS message
|
|
31
|
+
*/
|
|
32
|
+
interface SendMessageRequest {
|
|
33
|
+
/**
|
|
34
|
+
* Destination phone number in E.164 format (e.g., +15551234567)
|
|
35
|
+
*/
|
|
36
|
+
to: string;
|
|
37
|
+
/**
|
|
38
|
+
* Message content (max 160 chars per segment)
|
|
39
|
+
*/
|
|
40
|
+
text: string;
|
|
41
|
+
/**
|
|
42
|
+
* Sender ID or phone number (optional, uses default if not provided)
|
|
43
|
+
* For international: 2-11 alphanumeric characters
|
|
44
|
+
* For US/Canada: Your verified toll-free number
|
|
45
|
+
*/
|
|
46
|
+
from?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Message status values
|
|
50
|
+
*/
|
|
51
|
+
type MessageStatus = "queued" | "sending" | "sent" | "delivered" | "failed";
|
|
52
|
+
/**
|
|
53
|
+
* A sent or received SMS message
|
|
54
|
+
*/
|
|
55
|
+
interface Message {
|
|
56
|
+
/**
|
|
57
|
+
* Unique message identifier
|
|
58
|
+
*/
|
|
59
|
+
id: string;
|
|
60
|
+
/**
|
|
61
|
+
* Destination phone number
|
|
62
|
+
*/
|
|
63
|
+
to: string;
|
|
64
|
+
/**
|
|
65
|
+
* Sender ID or phone number
|
|
66
|
+
*/
|
|
67
|
+
from: string;
|
|
68
|
+
/**
|
|
69
|
+
* Message content
|
|
70
|
+
*/
|
|
71
|
+
text: string;
|
|
72
|
+
/**
|
|
73
|
+
* Current delivery status
|
|
74
|
+
*/
|
|
75
|
+
status: MessageStatus;
|
|
76
|
+
/**
|
|
77
|
+
* Error message if status is "failed"
|
|
78
|
+
*/
|
|
79
|
+
error?: string | null;
|
|
80
|
+
/**
|
|
81
|
+
* Number of SMS segments (1 per 160 chars)
|
|
82
|
+
*/
|
|
83
|
+
segments: number;
|
|
84
|
+
/**
|
|
85
|
+
* Credits charged for this message
|
|
86
|
+
*/
|
|
87
|
+
creditsUsed: number;
|
|
88
|
+
/**
|
|
89
|
+
* Whether this message was sent in sandbox mode
|
|
90
|
+
*/
|
|
91
|
+
isSandbox: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* ISO 8601 timestamp when the message was created
|
|
94
|
+
*/
|
|
95
|
+
createdAt: string;
|
|
96
|
+
/**
|
|
97
|
+
* ISO 8601 timestamp when the message was delivered (if applicable)
|
|
98
|
+
*/
|
|
99
|
+
deliveredAt?: string | null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Options for listing messages
|
|
103
|
+
*/
|
|
104
|
+
interface ListMessagesOptions {
|
|
105
|
+
/**
|
|
106
|
+
* Maximum number of messages to return (1-100)
|
|
107
|
+
* @default 50
|
|
108
|
+
*/
|
|
109
|
+
limit?: number;
|
|
110
|
+
/**
|
|
111
|
+
* Number of messages to skip for pagination
|
|
112
|
+
* @default 0
|
|
113
|
+
*/
|
|
114
|
+
offset?: number;
|
|
115
|
+
/**
|
|
116
|
+
* Filter by message status
|
|
117
|
+
*/
|
|
118
|
+
status?: MessageStatus;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Response from listing messages
|
|
122
|
+
*/
|
|
123
|
+
interface MessageListResponse {
|
|
124
|
+
/**
|
|
125
|
+
* Array of messages
|
|
126
|
+
*/
|
|
127
|
+
data: Message[];
|
|
128
|
+
/**
|
|
129
|
+
* Total count of messages returned
|
|
130
|
+
*/
|
|
131
|
+
count: number;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Request payload for scheduling an SMS message
|
|
135
|
+
*/
|
|
136
|
+
interface ScheduleMessageRequest {
|
|
137
|
+
/**
|
|
138
|
+
* Destination phone number in E.164 format (e.g., +15551234567)
|
|
139
|
+
*/
|
|
140
|
+
to: string;
|
|
141
|
+
/**
|
|
142
|
+
* Message content (max 160 chars per segment)
|
|
143
|
+
*/
|
|
144
|
+
text: string;
|
|
145
|
+
/**
|
|
146
|
+
* When to send the message (ISO 8601 format, must be > 1 minute in future)
|
|
147
|
+
*/
|
|
148
|
+
scheduledAt: string;
|
|
149
|
+
/**
|
|
150
|
+
* Sender ID (optional, for international destinations only)
|
|
151
|
+
* For US/Canada: This is ignored - toll-free number pool is used
|
|
152
|
+
*/
|
|
153
|
+
from?: string;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Scheduled message status values
|
|
157
|
+
*/
|
|
158
|
+
type ScheduledMessageStatus = "scheduled" | "sent" | "cancelled" | "failed";
|
|
159
|
+
/**
|
|
160
|
+
* A scheduled SMS message
|
|
161
|
+
*/
|
|
162
|
+
interface ScheduledMessage {
|
|
163
|
+
/**
|
|
164
|
+
* Unique message identifier
|
|
165
|
+
*/
|
|
166
|
+
id: string;
|
|
167
|
+
/**
|
|
168
|
+
* Destination phone number
|
|
169
|
+
*/
|
|
170
|
+
to: string;
|
|
171
|
+
/**
|
|
172
|
+
* Sender ID (if specified, for international messages)
|
|
173
|
+
*/
|
|
174
|
+
from?: string | null;
|
|
175
|
+
/**
|
|
176
|
+
* Message content
|
|
177
|
+
*/
|
|
178
|
+
text: string;
|
|
179
|
+
/**
|
|
180
|
+
* Current status
|
|
181
|
+
*/
|
|
182
|
+
status: ScheduledMessageStatus;
|
|
183
|
+
/**
|
|
184
|
+
* When the message is scheduled to send (ISO 8601)
|
|
185
|
+
*/
|
|
186
|
+
scheduledAt: string;
|
|
187
|
+
/**
|
|
188
|
+
* Credits reserved for this message
|
|
189
|
+
*/
|
|
190
|
+
creditsReserved: number;
|
|
191
|
+
/**
|
|
192
|
+
* Error message if status is "failed"
|
|
193
|
+
*/
|
|
194
|
+
error?: string | null;
|
|
195
|
+
/**
|
|
196
|
+
* ISO 8601 timestamp when scheduled
|
|
197
|
+
*/
|
|
198
|
+
createdAt: string;
|
|
199
|
+
/**
|
|
200
|
+
* ISO 8601 timestamp when cancelled (if applicable)
|
|
201
|
+
*/
|
|
202
|
+
cancelledAt?: string | null;
|
|
203
|
+
/**
|
|
204
|
+
* ISO 8601 timestamp when sent (if applicable)
|
|
205
|
+
*/
|
|
206
|
+
sentAt?: string | null;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Options for listing scheduled messages
|
|
210
|
+
*/
|
|
211
|
+
interface ListScheduledMessagesOptions {
|
|
212
|
+
/**
|
|
213
|
+
* Maximum number of messages to return (1-100)
|
|
214
|
+
* @default 50
|
|
215
|
+
*/
|
|
216
|
+
limit?: number;
|
|
217
|
+
/**
|
|
218
|
+
* Number of messages to skip for pagination
|
|
219
|
+
* @default 0
|
|
220
|
+
*/
|
|
221
|
+
offset?: number;
|
|
222
|
+
/**
|
|
223
|
+
* Filter by status
|
|
224
|
+
*/
|
|
225
|
+
status?: ScheduledMessageStatus;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Response from listing scheduled messages
|
|
229
|
+
*/
|
|
230
|
+
interface ScheduledMessageListResponse {
|
|
231
|
+
/**
|
|
232
|
+
* Array of scheduled messages
|
|
233
|
+
*/
|
|
234
|
+
data: ScheduledMessage[];
|
|
235
|
+
/**
|
|
236
|
+
* Total count of scheduled messages matching the filter
|
|
237
|
+
*/
|
|
238
|
+
count: number;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Response from cancelling a scheduled message
|
|
242
|
+
*/
|
|
243
|
+
interface CancelledMessageResponse {
|
|
244
|
+
/**
|
|
245
|
+
* Message ID
|
|
246
|
+
*/
|
|
247
|
+
id: string;
|
|
248
|
+
/**
|
|
249
|
+
* Status (always "cancelled")
|
|
250
|
+
*/
|
|
251
|
+
status: "cancelled";
|
|
252
|
+
/**
|
|
253
|
+
* Credits refunded
|
|
254
|
+
*/
|
|
255
|
+
creditsRefunded: number;
|
|
256
|
+
/**
|
|
257
|
+
* When the message was cancelled
|
|
258
|
+
*/
|
|
259
|
+
cancelledAt: string;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* A single message in a batch request
|
|
263
|
+
*/
|
|
264
|
+
interface BatchMessageItem {
|
|
265
|
+
/**
|
|
266
|
+
* Destination phone number in E.164 format
|
|
267
|
+
*/
|
|
268
|
+
to: string;
|
|
269
|
+
/**
|
|
270
|
+
* Message content
|
|
271
|
+
*/
|
|
272
|
+
text: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Request payload for sending batch messages
|
|
276
|
+
*/
|
|
277
|
+
interface BatchMessageRequest {
|
|
278
|
+
/**
|
|
279
|
+
* Array of messages to send (max 1000)
|
|
280
|
+
*/
|
|
281
|
+
messages: BatchMessageItem[];
|
|
282
|
+
/**
|
|
283
|
+
* Sender ID (optional, for international destinations only)
|
|
284
|
+
* For US/Canada destinations: This is ignored - toll-free number pool is used
|
|
285
|
+
*/
|
|
286
|
+
from?: string;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Result for a single message in a batch
|
|
290
|
+
*/
|
|
291
|
+
interface BatchMessageResult {
|
|
292
|
+
/**
|
|
293
|
+
* Message ID (if successful)
|
|
294
|
+
*/
|
|
295
|
+
id?: string;
|
|
296
|
+
/**
|
|
297
|
+
* Destination phone number
|
|
298
|
+
*/
|
|
299
|
+
to: string;
|
|
300
|
+
/**
|
|
301
|
+
* Status of this message
|
|
302
|
+
*/
|
|
303
|
+
status: "queued" | "failed";
|
|
304
|
+
/**
|
|
305
|
+
* Error message (if failed)
|
|
306
|
+
*/
|
|
307
|
+
error?: string;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Batch status values
|
|
311
|
+
*/
|
|
312
|
+
type BatchStatus = "processing" | "completed" | "partial_failure";
|
|
313
|
+
/**
|
|
314
|
+
* Response from sending batch messages
|
|
315
|
+
*/
|
|
316
|
+
interface BatchMessageResponse {
|
|
317
|
+
/**
|
|
318
|
+
* Unique batch identifier
|
|
319
|
+
*/
|
|
320
|
+
batchId: string;
|
|
321
|
+
/**
|
|
322
|
+
* Current batch status
|
|
323
|
+
*/
|
|
324
|
+
status: BatchStatus;
|
|
325
|
+
/**
|
|
326
|
+
* Total number of messages in batch
|
|
327
|
+
*/
|
|
328
|
+
total: number;
|
|
329
|
+
/**
|
|
330
|
+
* Number of messages queued successfully
|
|
331
|
+
*/
|
|
332
|
+
queued: number;
|
|
333
|
+
/**
|
|
334
|
+
* Number of messages sent
|
|
335
|
+
*/
|
|
336
|
+
sent: number;
|
|
337
|
+
/**
|
|
338
|
+
* Number of messages that failed
|
|
339
|
+
*/
|
|
340
|
+
failed: number;
|
|
341
|
+
/**
|
|
342
|
+
* Total credits used
|
|
343
|
+
*/
|
|
344
|
+
creditsUsed: number;
|
|
345
|
+
/**
|
|
346
|
+
* Individual message results
|
|
347
|
+
*/
|
|
348
|
+
messages: BatchMessageResult[];
|
|
349
|
+
/**
|
|
350
|
+
* When the batch was created
|
|
351
|
+
*/
|
|
352
|
+
createdAt: string;
|
|
353
|
+
/**
|
|
354
|
+
* When the batch completed (if applicable)
|
|
355
|
+
*/
|
|
356
|
+
completedAt?: string | null;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Options for listing batches
|
|
360
|
+
*/
|
|
361
|
+
interface ListBatchesOptions {
|
|
362
|
+
/**
|
|
363
|
+
* Maximum number of batches to return (1-100)
|
|
364
|
+
* @default 50
|
|
365
|
+
*/
|
|
366
|
+
limit?: number;
|
|
367
|
+
/**
|
|
368
|
+
* Number of batches to skip for pagination
|
|
369
|
+
* @default 0
|
|
370
|
+
*/
|
|
371
|
+
offset?: number;
|
|
372
|
+
/**
|
|
373
|
+
* Filter by status
|
|
374
|
+
*/
|
|
375
|
+
status?: BatchStatus;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Response from listing batches
|
|
379
|
+
*/
|
|
380
|
+
interface BatchListResponse {
|
|
381
|
+
/**
|
|
382
|
+
* Array of batches
|
|
383
|
+
*/
|
|
384
|
+
data: BatchMessageResponse[];
|
|
385
|
+
/**
|
|
386
|
+
* Total count of batches
|
|
387
|
+
*/
|
|
388
|
+
count: number;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Error codes returned by the Sendly API
|
|
392
|
+
*/
|
|
393
|
+
type SendlyErrorCode = "invalid_request" | "unauthorized" | "invalid_auth_format" | "invalid_key_format" | "invalid_api_key" | "key_revoked" | "key_expired" | "insufficient_permissions" | "insufficient_credits" | "unsupported_destination" | "not_found" | "rate_limit_exceeded" | "internal_error";
|
|
394
|
+
/**
|
|
395
|
+
* Error response from the Sendly API
|
|
396
|
+
*/
|
|
397
|
+
interface ApiErrorResponse {
|
|
398
|
+
/**
|
|
399
|
+
* Machine-readable error code
|
|
400
|
+
*/
|
|
401
|
+
error: SendlyErrorCode;
|
|
402
|
+
/**
|
|
403
|
+
* Human-readable error message
|
|
404
|
+
*/
|
|
405
|
+
message: string;
|
|
406
|
+
/**
|
|
407
|
+
* Credits needed (for insufficient_credits errors)
|
|
408
|
+
*/
|
|
409
|
+
creditsNeeded?: number;
|
|
410
|
+
/**
|
|
411
|
+
* Current credit balance (for insufficient_credits errors)
|
|
412
|
+
*/
|
|
413
|
+
currentBalance?: number;
|
|
414
|
+
/**
|
|
415
|
+
* Seconds to wait before retrying (for rate_limit_exceeded errors)
|
|
416
|
+
*/
|
|
417
|
+
retryAfter?: number;
|
|
418
|
+
/**
|
|
419
|
+
* Additional error context
|
|
420
|
+
*/
|
|
421
|
+
[key: string]: unknown;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* HTTP request options
|
|
425
|
+
*/
|
|
426
|
+
interface RequestOptions {
|
|
427
|
+
/**
|
|
428
|
+
* HTTP method
|
|
429
|
+
*/
|
|
430
|
+
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
431
|
+
/**
|
|
432
|
+
* Request path (without base URL)
|
|
433
|
+
*/
|
|
434
|
+
path: string;
|
|
435
|
+
/**
|
|
436
|
+
* Request body (will be JSON serialized)
|
|
437
|
+
*/
|
|
438
|
+
body?: Record<string, unknown>;
|
|
439
|
+
/**
|
|
440
|
+
* Query parameters
|
|
441
|
+
*/
|
|
442
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
443
|
+
/**
|
|
444
|
+
* Additional headers
|
|
445
|
+
*/
|
|
446
|
+
headers?: Record<string, string>;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Rate limit information from response headers
|
|
450
|
+
*/
|
|
451
|
+
interface RateLimitInfo {
|
|
452
|
+
/**
|
|
453
|
+
* Maximum requests allowed per window
|
|
454
|
+
*/
|
|
455
|
+
limit: number;
|
|
456
|
+
/**
|
|
457
|
+
* Remaining requests in current window
|
|
458
|
+
*/
|
|
459
|
+
remaining: number;
|
|
460
|
+
/**
|
|
461
|
+
* Seconds until the rate limit resets
|
|
462
|
+
*/
|
|
463
|
+
reset: number;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Pricing tier for SMS destinations
|
|
467
|
+
*/
|
|
468
|
+
type PricingTier = "domestic" | "tier1" | "tier2" | "tier3";
|
|
469
|
+
/**
|
|
470
|
+
* Credits required per SMS segment by tier
|
|
471
|
+
*/
|
|
472
|
+
declare const CREDITS_PER_SMS: Record<PricingTier, number>;
|
|
473
|
+
/**
|
|
474
|
+
* Supported country codes organized by pricing tier
|
|
475
|
+
*/
|
|
476
|
+
declare const SUPPORTED_COUNTRIES: Record<PricingTier, string[]>;
|
|
477
|
+
/**
|
|
478
|
+
* All supported country codes
|
|
479
|
+
*/
|
|
480
|
+
declare const ALL_SUPPORTED_COUNTRIES: string[];
|
|
481
|
+
/**
|
|
482
|
+
* Test phone numbers for sandbox mode
|
|
483
|
+
*/
|
|
484
|
+
declare const SANDBOX_TEST_NUMBERS: {
|
|
485
|
+
/** Always succeeds instantly */
|
|
486
|
+
readonly SUCCESS: "+15550001234";
|
|
487
|
+
/** Succeeds after 10 second delay */
|
|
488
|
+
readonly DELAYED: "+15550001010";
|
|
489
|
+
/** Fails with invalid_number error */
|
|
490
|
+
readonly INVALID: "+15550001001";
|
|
491
|
+
/** Fails with carrier_rejected error after 2 seconds */
|
|
492
|
+
readonly REJECTED: "+15550001002";
|
|
493
|
+
/** Fails with rate_limit_exceeded error */
|
|
494
|
+
readonly RATE_LIMITED: "+15550001003";
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* HTTP Client Utility
|
|
499
|
+
* @packageDocumentation
|
|
500
|
+
*/
|
|
501
|
+
|
|
502
|
+
interface HttpClientConfig {
|
|
503
|
+
apiKey: string;
|
|
504
|
+
baseUrl: string;
|
|
505
|
+
timeout: number;
|
|
506
|
+
maxRetries: number;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* HTTP client for making API requests
|
|
510
|
+
*/
|
|
511
|
+
declare class HttpClient {
|
|
512
|
+
private readonly config;
|
|
513
|
+
private rateLimitInfo?;
|
|
514
|
+
constructor(config: Partial<HttpClientConfig> & {
|
|
515
|
+
apiKey: string;
|
|
516
|
+
});
|
|
517
|
+
/**
|
|
518
|
+
* Validate API key format
|
|
519
|
+
*/
|
|
520
|
+
private isValidApiKey;
|
|
521
|
+
/**
|
|
522
|
+
* Get current rate limit info
|
|
523
|
+
*/
|
|
524
|
+
getRateLimitInfo(): RateLimitInfo | undefined;
|
|
525
|
+
/**
|
|
526
|
+
* Check if we're using a test key
|
|
527
|
+
*/
|
|
528
|
+
isTestMode(): boolean;
|
|
529
|
+
/**
|
|
530
|
+
* Make an HTTP request to the API
|
|
531
|
+
*/
|
|
532
|
+
request<T>(options: RequestOptions): Promise<T>;
|
|
533
|
+
/**
|
|
534
|
+
* Execute the HTTP request
|
|
535
|
+
*/
|
|
536
|
+
private executeRequest;
|
|
537
|
+
/**
|
|
538
|
+
* Parse the response body
|
|
539
|
+
*/
|
|
540
|
+
private parseResponse;
|
|
541
|
+
/**
|
|
542
|
+
* Build the full URL with query parameters
|
|
543
|
+
*/
|
|
544
|
+
private buildUrl;
|
|
545
|
+
/**
|
|
546
|
+
* Build request headers
|
|
547
|
+
*/
|
|
548
|
+
private buildHeaders;
|
|
549
|
+
/**
|
|
550
|
+
* Update rate limit info from response headers
|
|
551
|
+
*/
|
|
552
|
+
private updateRateLimitInfo;
|
|
553
|
+
/**
|
|
554
|
+
* Calculate exponential backoff time
|
|
555
|
+
*/
|
|
556
|
+
private calculateBackoff;
|
|
557
|
+
/**
|
|
558
|
+
* Sleep for a given number of milliseconds
|
|
559
|
+
*/
|
|
560
|
+
private sleep;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Messages Resource
|
|
565
|
+
* @packageDocumentation
|
|
566
|
+
*/
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Messages API resource
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```typescript
|
|
573
|
+
* // Send a message
|
|
574
|
+
* const message = await sendly.messages.send({
|
|
575
|
+
* to: '+15551234567',
|
|
576
|
+
* text: 'Hello from Sendly!'
|
|
577
|
+
* });
|
|
578
|
+
*
|
|
579
|
+
* // List recent messages
|
|
580
|
+
* const { data: messages } = await sendly.messages.list({ limit: 10 });
|
|
581
|
+
*
|
|
582
|
+
* // Get a specific message
|
|
583
|
+
* const message = await sendly.messages.get('msg_xxx');
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
declare class MessagesResource {
|
|
587
|
+
private readonly http;
|
|
588
|
+
constructor(http: HttpClient);
|
|
589
|
+
/**
|
|
590
|
+
* Send an SMS message
|
|
591
|
+
*
|
|
592
|
+
* @param request - Message details
|
|
593
|
+
* @returns The created message
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```typescript
|
|
597
|
+
* const message = await sendly.messages.send({
|
|
598
|
+
* to: '+15551234567',
|
|
599
|
+
* text: 'Your verification code is: 123456'
|
|
600
|
+
* });
|
|
601
|
+
*
|
|
602
|
+
* console.log(message.id); // msg_xxx
|
|
603
|
+
* console.log(message.status); // 'queued'
|
|
604
|
+
* console.log(message.segments); // 1
|
|
605
|
+
* ```
|
|
606
|
+
*
|
|
607
|
+
* @throws {ValidationError} If the request is invalid
|
|
608
|
+
* @throws {InsufficientCreditsError} If credit balance is too low
|
|
609
|
+
* @throws {AuthenticationError} If the API key is invalid
|
|
610
|
+
* @throws {RateLimitError} If rate limit is exceeded
|
|
611
|
+
*/
|
|
612
|
+
send(request: SendMessageRequest): Promise<Message>;
|
|
613
|
+
/**
|
|
614
|
+
* List sent messages
|
|
615
|
+
*
|
|
616
|
+
* @param options - List options
|
|
617
|
+
* @returns Paginated list of messages
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```typescript
|
|
621
|
+
* // Get last 50 messages (default)
|
|
622
|
+
* const { data: messages, count } = await sendly.messages.list();
|
|
623
|
+
*
|
|
624
|
+
* // Get last 10 messages
|
|
625
|
+
* const { data: messages } = await sendly.messages.list({ limit: 10 });
|
|
626
|
+
*
|
|
627
|
+
* // Iterate through messages
|
|
628
|
+
* for (const msg of messages) {
|
|
629
|
+
* console.log(`${msg.to}: ${msg.status}`);
|
|
630
|
+
* }
|
|
631
|
+
* ```
|
|
632
|
+
*
|
|
633
|
+
* @throws {AuthenticationError} If the API key is invalid
|
|
634
|
+
* @throws {RateLimitError} If rate limit is exceeded
|
|
635
|
+
*/
|
|
636
|
+
list(options?: ListMessagesOptions): Promise<MessageListResponse>;
|
|
637
|
+
/**
|
|
638
|
+
* Get a specific message by ID
|
|
639
|
+
*
|
|
640
|
+
* @param id - Message ID
|
|
641
|
+
* @returns The message details
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```typescript
|
|
645
|
+
* const message = await sendly.messages.get('msg_xxx');
|
|
646
|
+
*
|
|
647
|
+
* console.log(message.status); // 'delivered'
|
|
648
|
+
* console.log(message.deliveredAt); // '2025-01-15T10:30:00Z'
|
|
649
|
+
* ```
|
|
650
|
+
*
|
|
651
|
+
* @throws {NotFoundError} If the message doesn't exist
|
|
652
|
+
* @throws {AuthenticationError} If the API key is invalid
|
|
653
|
+
* @throws {RateLimitError} If rate limit is exceeded
|
|
654
|
+
*/
|
|
655
|
+
get(id: string): Promise<Message>;
|
|
656
|
+
/**
|
|
657
|
+
* Iterate through all messages with automatic pagination
|
|
658
|
+
*
|
|
659
|
+
* @param options - List options (limit is used as batch size)
|
|
660
|
+
* @yields Message objects one at a time
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```typescript
|
|
664
|
+
* // Iterate through all messages
|
|
665
|
+
* for await (const message of sendly.messages.listAll()) {
|
|
666
|
+
* console.log(`${message.id}: ${message.status}`);
|
|
667
|
+
* }
|
|
668
|
+
*
|
|
669
|
+
* // With custom batch size
|
|
670
|
+
* for await (const message of sendly.messages.listAll({ limit: 100 })) {
|
|
671
|
+
* console.log(message.to);
|
|
672
|
+
* }
|
|
673
|
+
* ```
|
|
674
|
+
*
|
|
675
|
+
* @throws {AuthenticationError} If the API key is invalid
|
|
676
|
+
* @throws {RateLimitError} If rate limit is exceeded
|
|
677
|
+
*/
|
|
678
|
+
listAll(options?: ListMessagesOptions): AsyncGenerator<Message>;
|
|
679
|
+
/**
|
|
680
|
+
* Schedule an SMS message for future delivery
|
|
681
|
+
*
|
|
682
|
+
* @param request - Schedule request details
|
|
683
|
+
* @returns The scheduled message
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```typescript
|
|
687
|
+
* const scheduled = await sendly.messages.schedule({
|
|
688
|
+
* to: '+15551234567',
|
|
689
|
+
* text: 'Your appointment reminder!',
|
|
690
|
+
* scheduledAt: '2025-01-20T10:00:00Z'
|
|
691
|
+
* });
|
|
692
|
+
*
|
|
693
|
+
* console.log(scheduled.id); // msg_xxx
|
|
694
|
+
* console.log(scheduled.status); // 'scheduled'
|
|
695
|
+
* console.log(scheduled.scheduledAt); // '2025-01-20T10:00:00Z'
|
|
696
|
+
* ```
|
|
697
|
+
*
|
|
698
|
+
* @throws {ValidationError} If the request is invalid
|
|
699
|
+
* @throws {InsufficientCreditsError} If credit balance is too low
|
|
700
|
+
* @throws {AuthenticationError} If the API key is invalid
|
|
701
|
+
*/
|
|
702
|
+
schedule(request: ScheduleMessageRequest): Promise<ScheduledMessage>;
|
|
703
|
+
/**
|
|
704
|
+
* List scheduled messages
|
|
705
|
+
*
|
|
706
|
+
* @param options - List options
|
|
707
|
+
* @returns Paginated list of scheduled messages
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```typescript
|
|
711
|
+
* const { data: scheduled } = await sendly.messages.listScheduled();
|
|
712
|
+
*
|
|
713
|
+
* for (const msg of scheduled) {
|
|
714
|
+
* console.log(`${msg.to}: ${msg.scheduledAt}`);
|
|
715
|
+
* }
|
|
716
|
+
* ```
|
|
717
|
+
*/
|
|
718
|
+
listScheduled(options?: ListScheduledMessagesOptions): Promise<ScheduledMessageListResponse>;
|
|
719
|
+
/**
|
|
720
|
+
* Get a specific scheduled message by ID
|
|
721
|
+
*
|
|
722
|
+
* @param id - Message ID
|
|
723
|
+
* @returns The scheduled message details
|
|
724
|
+
*
|
|
725
|
+
* @example
|
|
726
|
+
* ```typescript
|
|
727
|
+
* const scheduled = await sendly.messages.getScheduled('msg_xxx');
|
|
728
|
+
* console.log(scheduled.scheduledAt);
|
|
729
|
+
* ```
|
|
730
|
+
*/
|
|
731
|
+
getScheduled(id: string): Promise<ScheduledMessage>;
|
|
732
|
+
/**
|
|
733
|
+
* Cancel a scheduled message
|
|
734
|
+
*
|
|
735
|
+
* @param id - Message ID to cancel
|
|
736
|
+
* @returns Cancellation confirmation with refunded credits
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* const result = await sendly.messages.cancelScheduled('msg_xxx');
|
|
741
|
+
*
|
|
742
|
+
* console.log(result.status); // 'cancelled'
|
|
743
|
+
* console.log(result.creditsRefunded); // 1
|
|
744
|
+
* ```
|
|
745
|
+
*
|
|
746
|
+
* @throws {NotFoundError} If the message doesn't exist
|
|
747
|
+
* @throws {ValidationError} If the message is not cancellable
|
|
748
|
+
*/
|
|
749
|
+
cancelScheduled(id: string): Promise<CancelledMessageResponse>;
|
|
750
|
+
/**
|
|
751
|
+
* Send multiple SMS messages in a single batch
|
|
752
|
+
*
|
|
753
|
+
* @param request - Batch request with array of messages
|
|
754
|
+
* @returns Batch response with individual message results
|
|
755
|
+
*
|
|
756
|
+
* @example
|
|
757
|
+
* ```typescript
|
|
758
|
+
* const batch = await sendly.messages.sendBatch({
|
|
759
|
+
* messages: [
|
|
760
|
+
* { to: '+15551234567', text: 'Hello User 1!' },
|
|
761
|
+
* { to: '+15559876543', text: 'Hello User 2!' }
|
|
762
|
+
* ]
|
|
763
|
+
* });
|
|
764
|
+
*
|
|
765
|
+
* console.log(batch.batchId); // batch_xxx
|
|
766
|
+
* console.log(batch.queued); // 2
|
|
767
|
+
* console.log(batch.creditsUsed); // 2
|
|
768
|
+
* ```
|
|
769
|
+
*
|
|
770
|
+
* @throws {ValidationError} If any message is invalid
|
|
771
|
+
* @throws {InsufficientCreditsError} If credit balance is too low
|
|
772
|
+
*/
|
|
773
|
+
sendBatch(request: BatchMessageRequest): Promise<BatchMessageResponse>;
|
|
774
|
+
/**
|
|
775
|
+
* Get batch status and results
|
|
776
|
+
*
|
|
777
|
+
* @param batchId - Batch ID
|
|
778
|
+
* @returns Batch details with message results
|
|
779
|
+
*
|
|
780
|
+
* @example
|
|
781
|
+
* ```typescript
|
|
782
|
+
* const batch = await sendly.messages.getBatch('batch_xxx');
|
|
783
|
+
*
|
|
784
|
+
* console.log(batch.status); // 'completed'
|
|
785
|
+
* console.log(batch.sent); // 2
|
|
786
|
+
* console.log(batch.failed); // 0
|
|
787
|
+
* ```
|
|
788
|
+
*/
|
|
789
|
+
getBatch(batchId: string): Promise<BatchMessageResponse>;
|
|
790
|
+
/**
|
|
791
|
+
* List message batches
|
|
792
|
+
*
|
|
793
|
+
* @param options - List options
|
|
794
|
+
* @returns Paginated list of batches
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```typescript
|
|
798
|
+
* const { data: batches } = await sendly.messages.listBatches();
|
|
799
|
+
*
|
|
800
|
+
* for (const batch of batches) {
|
|
801
|
+
* console.log(`${batch.batchId}: ${batch.status}`);
|
|
802
|
+
* }
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
listBatches(options?: ListBatchesOptions): Promise<BatchListResponse>;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* Sendly Client
|
|
810
|
+
* @packageDocumentation
|
|
811
|
+
*/
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Sendly API Client
|
|
815
|
+
*
|
|
816
|
+
* The main entry point for interacting with the Sendly SMS API.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* ```typescript
|
|
820
|
+
* import Sendly from '@sendly/node';
|
|
821
|
+
*
|
|
822
|
+
* // Initialize with API key
|
|
823
|
+
* const sendly = new Sendly('sk_live_v1_your_api_key');
|
|
824
|
+
*
|
|
825
|
+
* // Send an SMS
|
|
826
|
+
* const message = await sendly.messages.send({
|
|
827
|
+
* to: '+15551234567',
|
|
828
|
+
* text: 'Hello from Sendly!'
|
|
829
|
+
* });
|
|
830
|
+
*
|
|
831
|
+
* console.log(message.id);
|
|
832
|
+
* ```
|
|
833
|
+
*
|
|
834
|
+
* @example
|
|
835
|
+
* ```typescript
|
|
836
|
+
* // Initialize with custom configuration
|
|
837
|
+
* const sendly = new Sendly({
|
|
838
|
+
* apiKey: 'sk_live_v1_your_api_key',
|
|
839
|
+
* timeout: 60000,
|
|
840
|
+
* maxRetries: 5
|
|
841
|
+
* });
|
|
842
|
+
* ```
|
|
843
|
+
*/
|
|
844
|
+
declare class Sendly {
|
|
845
|
+
/**
|
|
846
|
+
* Messages API resource
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```typescript
|
|
850
|
+
* // Send a message
|
|
851
|
+
* await sendly.messages.send({ to: '+1555...', text: 'Hello!' });
|
|
852
|
+
*
|
|
853
|
+
* // List messages
|
|
854
|
+
* const { data } = await sendly.messages.list({ limit: 10 });
|
|
855
|
+
*
|
|
856
|
+
* // Get a message
|
|
857
|
+
* const msg = await sendly.messages.get('msg_xxx');
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
860
|
+
readonly messages: MessagesResource;
|
|
861
|
+
private readonly http;
|
|
862
|
+
private readonly config;
|
|
863
|
+
/**
|
|
864
|
+
* Create a new Sendly client
|
|
865
|
+
*
|
|
866
|
+
* @param configOrApiKey - API key string or configuration object
|
|
867
|
+
*/
|
|
868
|
+
constructor(configOrApiKey: string | SendlyConfig);
|
|
869
|
+
/**
|
|
870
|
+
* Check if the client is using a test API key
|
|
871
|
+
*
|
|
872
|
+
* @returns true if using a test key (sk_test_v1_xxx)
|
|
873
|
+
*
|
|
874
|
+
* @example
|
|
875
|
+
* ```typescript
|
|
876
|
+
* if (sendly.isTestMode()) {
|
|
877
|
+
* console.log('Running in test mode');
|
|
878
|
+
* }
|
|
879
|
+
* ```
|
|
880
|
+
*/
|
|
881
|
+
isTestMode(): boolean;
|
|
882
|
+
/**
|
|
883
|
+
* Get current rate limit information
|
|
884
|
+
*
|
|
885
|
+
* Returns the rate limit info from the most recent API request.
|
|
886
|
+
*
|
|
887
|
+
* @returns Rate limit info or undefined if no requests have been made
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* ```typescript
|
|
891
|
+
* await sendly.messages.send({ to: '+1555...', text: 'Hello!' });
|
|
892
|
+
*
|
|
893
|
+
* const rateLimit = sendly.getRateLimitInfo();
|
|
894
|
+
* if (rateLimit) {
|
|
895
|
+
* console.log(`${rateLimit.remaining}/${rateLimit.limit} requests remaining`);
|
|
896
|
+
* console.log(`Resets in ${rateLimit.reset} seconds`);
|
|
897
|
+
* }
|
|
898
|
+
* ```
|
|
899
|
+
*/
|
|
900
|
+
getRateLimitInfo(): RateLimitInfo | undefined;
|
|
901
|
+
/**
|
|
902
|
+
* Get the configured base URL
|
|
903
|
+
*/
|
|
904
|
+
getBaseUrl(): string;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Sendly SDK Error Classes
|
|
909
|
+
* @packageDocumentation
|
|
910
|
+
*/
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Base error class for all Sendly SDK errors
|
|
914
|
+
*/
|
|
915
|
+
declare class SendlyError extends Error {
|
|
916
|
+
/**
|
|
917
|
+
* Machine-readable error code
|
|
918
|
+
*/
|
|
919
|
+
readonly code: SendlyErrorCode;
|
|
920
|
+
/**
|
|
921
|
+
* HTTP status code (if applicable)
|
|
922
|
+
*/
|
|
923
|
+
readonly statusCode?: number;
|
|
924
|
+
/**
|
|
925
|
+
* Raw API response (if applicable)
|
|
926
|
+
*/
|
|
927
|
+
readonly response?: ApiErrorResponse;
|
|
928
|
+
constructor(message: string, code: SendlyErrorCode, statusCode?: number, response?: ApiErrorResponse);
|
|
929
|
+
/**
|
|
930
|
+
* Create a SendlyError from an API response
|
|
931
|
+
*/
|
|
932
|
+
static fromResponse(statusCode: number, response: ApiErrorResponse): SendlyError;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Thrown when authentication fails
|
|
936
|
+
*/
|
|
937
|
+
declare class AuthenticationError extends SendlyError {
|
|
938
|
+
constructor(message: string, code?: SendlyErrorCode, statusCode?: number, response?: ApiErrorResponse);
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Thrown when rate limit is exceeded
|
|
942
|
+
*/
|
|
943
|
+
declare class RateLimitError extends SendlyError {
|
|
944
|
+
/**
|
|
945
|
+
* Seconds to wait before retrying
|
|
946
|
+
*/
|
|
947
|
+
readonly retryAfter: number;
|
|
948
|
+
constructor(message: string, retryAfter: number, statusCode?: number, response?: ApiErrorResponse);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Thrown when credit balance is insufficient
|
|
952
|
+
*/
|
|
953
|
+
declare class InsufficientCreditsError extends SendlyError {
|
|
954
|
+
/**
|
|
955
|
+
* Credits needed for the operation
|
|
956
|
+
*/
|
|
957
|
+
readonly creditsNeeded: number;
|
|
958
|
+
/**
|
|
959
|
+
* Current credit balance
|
|
960
|
+
*/
|
|
961
|
+
readonly currentBalance: number;
|
|
962
|
+
constructor(message: string, creditsNeeded: number, currentBalance: number, statusCode?: number, response?: ApiErrorResponse);
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Thrown when request validation fails
|
|
966
|
+
*/
|
|
967
|
+
declare class ValidationError extends SendlyError {
|
|
968
|
+
constructor(message: string, code?: SendlyErrorCode, statusCode?: number, response?: ApiErrorResponse);
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Thrown when a resource is not found
|
|
972
|
+
*/
|
|
973
|
+
declare class NotFoundError extends SendlyError {
|
|
974
|
+
constructor(message: string, statusCode?: number, response?: ApiErrorResponse);
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Thrown when a network or connection error occurs
|
|
978
|
+
*/
|
|
979
|
+
declare class NetworkError extends SendlyError {
|
|
980
|
+
constructor(message: string, cause?: Error);
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Thrown when a request times out
|
|
984
|
+
*/
|
|
985
|
+
declare class TimeoutError extends SendlyError {
|
|
986
|
+
constructor(message?: string);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Input Validation Utilities
|
|
991
|
+
* @packageDocumentation
|
|
992
|
+
*/
|
|
993
|
+
/**
|
|
994
|
+
* Validate phone number format (E.164)
|
|
995
|
+
*/
|
|
996
|
+
declare function validatePhoneNumber(phone: string): void;
|
|
997
|
+
/**
|
|
998
|
+
* Validate message text
|
|
999
|
+
*/
|
|
1000
|
+
declare function validateMessageText(text: string): void;
|
|
1001
|
+
/**
|
|
1002
|
+
* Validate sender ID
|
|
1003
|
+
*/
|
|
1004
|
+
declare function validateSenderId(from: string): void;
|
|
1005
|
+
/**
|
|
1006
|
+
* Get country code from phone number
|
|
1007
|
+
*/
|
|
1008
|
+
declare function getCountryFromPhone(phone: string): string | null;
|
|
1009
|
+
/**
|
|
1010
|
+
* Check if a country is supported
|
|
1011
|
+
*/
|
|
1012
|
+
declare function isCountrySupported(countryCode: string): boolean;
|
|
1013
|
+
/**
|
|
1014
|
+
* Calculate number of SMS segments for a message
|
|
1015
|
+
*/
|
|
1016
|
+
declare function calculateSegments(text: string): number;
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Webhook utilities for Sendly
|
|
1020
|
+
* Provides signature verification and event parsing
|
|
1021
|
+
* @packageDocumentation
|
|
1022
|
+
*/
|
|
1023
|
+
/**
|
|
1024
|
+
* Webhook event types
|
|
1025
|
+
*/
|
|
1026
|
+
type WebhookEventType = "message.queued" | "message.sent" | "message.delivered" | "message.failed" | "message.undelivered";
|
|
1027
|
+
/**
|
|
1028
|
+
* Message status in webhook events
|
|
1029
|
+
*/
|
|
1030
|
+
type WebhookMessageStatus = "queued" | "sent" | "delivered" | "failed" | "undelivered";
|
|
1031
|
+
/**
|
|
1032
|
+
* Webhook message data payload
|
|
1033
|
+
*/
|
|
1034
|
+
interface WebhookMessageData {
|
|
1035
|
+
/** Unique message identifier */
|
|
1036
|
+
messageId: string;
|
|
1037
|
+
/** Current message status */
|
|
1038
|
+
status: WebhookMessageStatus;
|
|
1039
|
+
/** Destination phone number */
|
|
1040
|
+
to: string;
|
|
1041
|
+
/** Sender ID or phone number */
|
|
1042
|
+
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;
|
|
1051
|
+
/** Number of SMS segments */
|
|
1052
|
+
segments: number;
|
|
1053
|
+
/** Credits charged */
|
|
1054
|
+
creditsUsed: number;
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Webhook event structure
|
|
1058
|
+
*/
|
|
1059
|
+
interface WebhookEvent {
|
|
1060
|
+
/** Unique event identifier */
|
|
1061
|
+
id: string;
|
|
1062
|
+
/** Event type */
|
|
1063
|
+
type: WebhookEventType;
|
|
1064
|
+
/** Event data payload */
|
|
1065
|
+
data: WebhookMessageData;
|
|
1066
|
+
/** ISO 8601 timestamp when event was created */
|
|
1067
|
+
createdAt: string;
|
|
1068
|
+
/** API version that generated this event */
|
|
1069
|
+
apiVersion: string;
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* Error thrown when webhook signature verification fails
|
|
1073
|
+
*/
|
|
1074
|
+
declare class WebhookSignatureError extends Error {
|
|
1075
|
+
constructor(message?: string);
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Verify a webhook signature from Sendly
|
|
1079
|
+
*
|
|
1080
|
+
* @param payload - Raw request body as string
|
|
1081
|
+
* @param signature - X-Sendly-Signature header value
|
|
1082
|
+
* @param secret - Your webhook secret from dashboard
|
|
1083
|
+
* @returns true if signature is valid
|
|
1084
|
+
*
|
|
1085
|
+
* @example
|
|
1086
|
+
* ```typescript
|
|
1087
|
+
* import { verifyWebhookSignature } from '@sendly/node';
|
|
1088
|
+
*
|
|
1089
|
+
* const isValid = verifyWebhookSignature(
|
|
1090
|
+
* req.body, // raw body string
|
|
1091
|
+
* req.headers['x-sendly-signature'],
|
|
1092
|
+
* process.env.WEBHOOK_SECRET
|
|
1093
|
+
* );
|
|
1094
|
+
*
|
|
1095
|
+
* if (!isValid) {
|
|
1096
|
+
* return res.status(401).send('Invalid signature');
|
|
1097
|
+
* }
|
|
1098
|
+
* ```
|
|
1099
|
+
*/
|
|
1100
|
+
declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;
|
|
1101
|
+
/**
|
|
1102
|
+
* Parse and verify a webhook event
|
|
1103
|
+
*
|
|
1104
|
+
* @param payload - Raw request body as string
|
|
1105
|
+
* @param signature - X-Sendly-Signature header value
|
|
1106
|
+
* @param secret - Your webhook secret from dashboard
|
|
1107
|
+
* @returns Parsed webhook event
|
|
1108
|
+
* @throws {WebhookSignatureError} If signature is invalid
|
|
1109
|
+
*
|
|
1110
|
+
* @example
|
|
1111
|
+
* ```typescript
|
|
1112
|
+
* import { parseWebhookEvent } from '@sendly/node';
|
|
1113
|
+
*
|
|
1114
|
+
* try {
|
|
1115
|
+
* const event = parseWebhookEvent(
|
|
1116
|
+
* req.body,
|
|
1117
|
+
* req.headers['x-sendly-signature'],
|
|
1118
|
+
* process.env.WEBHOOK_SECRET
|
|
1119
|
+
* );
|
|
1120
|
+
*
|
|
1121
|
+
* switch (event.type) {
|
|
1122
|
+
* case 'message.delivered':
|
|
1123
|
+
* console.log(`Message ${event.data.messageId} delivered!`);
|
|
1124
|
+
* break;
|
|
1125
|
+
* case 'message.failed':
|
|
1126
|
+
* console.log(`Message failed: ${event.data.error}`);
|
|
1127
|
+
* break;
|
|
1128
|
+
* }
|
|
1129
|
+
* } catch (err) {
|
|
1130
|
+
* if (err instanceof WebhookSignatureError) {
|
|
1131
|
+
* return res.status(401).send('Invalid signature');
|
|
1132
|
+
* }
|
|
1133
|
+
* throw err;
|
|
1134
|
+
* }
|
|
1135
|
+
* ```
|
|
1136
|
+
*/
|
|
1137
|
+
declare function parseWebhookEvent(payload: string, signature: string, secret: string): WebhookEvent;
|
|
1138
|
+
/**
|
|
1139
|
+
* Generate a webhook signature for testing purposes
|
|
1140
|
+
*
|
|
1141
|
+
* @param payload - The payload to sign
|
|
1142
|
+
* @param secret - The secret to use for signing
|
|
1143
|
+
* @returns Signature in format "sha256=..."
|
|
1144
|
+
*
|
|
1145
|
+
* @example
|
|
1146
|
+
* ```typescript
|
|
1147
|
+
* import { generateWebhookSignature } from '@sendly/node';
|
|
1148
|
+
*
|
|
1149
|
+
* // For testing your webhook handler
|
|
1150
|
+
* const testPayload = JSON.stringify({
|
|
1151
|
+
* id: 'evt_test',
|
|
1152
|
+
* type: 'message.delivered',
|
|
1153
|
+
* data: { messageId: 'msg_123', status: 'delivered' },
|
|
1154
|
+
* createdAt: new Date().toISOString(),
|
|
1155
|
+
* apiVersion: '2025-01-01'
|
|
1156
|
+
* });
|
|
1157
|
+
*
|
|
1158
|
+
* const signature = generateWebhookSignature(testPayload, 'test_secret');
|
|
1159
|
+
* ```
|
|
1160
|
+
*/
|
|
1161
|
+
declare function generateWebhookSignature(payload: string, secret: string): string;
|
|
1162
|
+
/**
|
|
1163
|
+
* Webhook utilities class (alternative API)
|
|
1164
|
+
*
|
|
1165
|
+
* @example
|
|
1166
|
+
* ```typescript
|
|
1167
|
+
* import { Webhooks } from '@sendly/node';
|
|
1168
|
+
*
|
|
1169
|
+
* const webhooks = new Webhooks('your_webhook_secret');
|
|
1170
|
+
*
|
|
1171
|
+
* // Verify signature
|
|
1172
|
+
* const isValid = webhooks.verify(payload, signature);
|
|
1173
|
+
*
|
|
1174
|
+
* // Parse event
|
|
1175
|
+
* const event = webhooks.parse(payload, signature);
|
|
1176
|
+
* ```
|
|
1177
|
+
*/
|
|
1178
|
+
declare class Webhooks {
|
|
1179
|
+
private readonly secret;
|
|
1180
|
+
/**
|
|
1181
|
+
* Create a new Webhooks instance
|
|
1182
|
+
* @param secret - Your webhook secret from the Sendly dashboard
|
|
1183
|
+
*/
|
|
1184
|
+
constructor(secret: string);
|
|
1185
|
+
/**
|
|
1186
|
+
* Verify a webhook signature
|
|
1187
|
+
* @param payload - Raw request body
|
|
1188
|
+
* @param signature - X-Sendly-Signature header
|
|
1189
|
+
*/
|
|
1190
|
+
verify(payload: string, signature: string): boolean;
|
|
1191
|
+
/**
|
|
1192
|
+
* Parse and verify a webhook event
|
|
1193
|
+
* @param payload - Raw request body
|
|
1194
|
+
* @param signature - X-Sendly-Signature header
|
|
1195
|
+
*/
|
|
1196
|
+
parse(payload: string, signature: string): WebhookEvent;
|
|
1197
|
+
/**
|
|
1198
|
+
* Generate a signature for testing
|
|
1199
|
+
* @param payload - Payload to sign
|
|
1200
|
+
*/
|
|
1201
|
+
sign(payload: string): string;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
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 };
|