@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.
@@ -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 };