@sendly/node 3.5.2 → 3.5.4

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 CHANGED
@@ -77,10 +77,17 @@ import Sendly from '@sendly/node';
77
77
 
78
78
  const sendly = new Sendly('sk_live_v1_xxx');
79
79
 
80
- // Basic usage
80
+ // Basic usage (marketing message - default)
81
81
  const message = await sendly.messages.send({
82
82
  to: '+15551234567',
83
- text: 'Your verification code is: 123456'
83
+ text: 'Check out our new features!'
84
+ });
85
+
86
+ // Transactional message (bypasses quiet hours)
87
+ const message = await sendly.messages.send({
88
+ to: '+15551234567',
89
+ text: 'Your verification code is: 123456',
90
+ messageType: 'transactional'
84
91
  });
85
92
 
86
93
  // With custom sender ID (international)
@@ -115,6 +122,54 @@ console.log(`Status: ${message.status}`);
115
122
  console.log(`Delivered: ${message.deliveredAt}`);
116
123
  ```
117
124
 
125
+ ### Scheduling Messages
126
+
127
+ ```typescript
128
+ // Schedule a message for future delivery
129
+ const scheduled = await sendly.messages.schedule({
130
+ to: '+15551234567',
131
+ text: 'Your appointment is tomorrow!',
132
+ scheduledAt: '2025-01-15T10:00:00Z'
133
+ });
134
+
135
+ console.log(`Scheduled: ${scheduled.id}`);
136
+ console.log(`Will send at: ${scheduled.scheduledAt}`);
137
+
138
+ // List scheduled messages
139
+ const { data: scheduledMessages } = await sendly.messages.listScheduled();
140
+
141
+ // Get a specific scheduled message
142
+ const msg = await sendly.messages.getScheduled('sched_xxx');
143
+
144
+ // Cancel a scheduled message (refunds credits)
145
+ const result = await sendly.messages.cancelScheduled('sched_xxx');
146
+ console.log(`Refunded: ${result.creditsRefunded} credits`);
147
+ ```
148
+
149
+ ### Batch Messages
150
+
151
+ ```typescript
152
+ // Send multiple messages in one API call (up to 1000)
153
+ const batch = await sendly.messages.sendBatch({
154
+ messages: [
155
+ { to: '+15551234567', text: 'Hello User 1!' },
156
+ { to: '+15559876543', text: 'Hello User 2!' },
157
+ { to: '+15551112222', text: 'Hello User 3!' }
158
+ ]
159
+ });
160
+
161
+ console.log(`Batch ID: ${batch.batchId}`);
162
+ console.log(`Queued: ${batch.queued}`);
163
+ console.log(`Failed: ${batch.failed}`);
164
+ console.log(`Credits used: ${batch.creditsUsed}`);
165
+
166
+ // Get batch status
167
+ const status = await sendly.messages.getBatch('batch_xxx');
168
+
169
+ // List all batches
170
+ const { data: batches } = await sendly.messages.listBatches();
171
+ ```
172
+
118
173
  ### Rate Limit Information
119
174
 
120
175
  ```typescript
@@ -147,6 +202,113 @@ const sendly = new Sendly({
147
202
  });
148
203
  ```
149
204
 
205
+ ## Webhooks
206
+
207
+ Manage webhook endpoints to receive real-time delivery status updates.
208
+
209
+ ```typescript
210
+ // Create a webhook endpoint
211
+ const webhook = await sendly.webhooks.create({
212
+ url: 'https://example.com/webhooks/sendly',
213
+ events: ['message.delivered', 'message.failed']
214
+ });
215
+
216
+ console.log(`Webhook ID: ${webhook.id}`);
217
+ console.log(`Secret: ${webhook.secret}`); // Store this securely!
218
+
219
+ // List all webhooks
220
+ const webhooks = await sendly.webhooks.list();
221
+
222
+ // Get a specific webhook
223
+ const wh = await sendly.webhooks.get('whk_xxx');
224
+
225
+ // Update a webhook
226
+ await sendly.webhooks.update('whk_xxx', {
227
+ url: 'https://new-endpoint.example.com/webhook',
228
+ events: ['message.delivered', 'message.failed', 'message.sent']
229
+ });
230
+
231
+ // Test a webhook (sends a test event)
232
+ const testResult = await sendly.webhooks.test('whk_xxx');
233
+ console.log(`Test ${testResult.success ? 'passed' : 'failed'}`);
234
+
235
+ // Rotate webhook secret
236
+ const rotation = await sendly.webhooks.rotateSecret('whk_xxx');
237
+ console.log(`New secret: ${rotation.secret}`);
238
+
239
+ // View delivery history
240
+ const deliveries = await sendly.webhooks.getDeliveries('whk_xxx');
241
+
242
+ // Retry a failed delivery
243
+ await sendly.webhooks.retryDelivery('whk_xxx', 'del_yyy');
244
+
245
+ // Delete a webhook
246
+ await sendly.webhooks.delete('whk_xxx');
247
+ ```
248
+
249
+ ### Verifying Webhook Signatures
250
+
251
+ ```typescript
252
+ import { Webhooks } from '@sendly/node';
253
+
254
+ const webhooks = new Webhooks('your_webhook_secret');
255
+
256
+ // In your webhook handler
257
+ app.post('/webhooks/sendly', (req, res) => {
258
+ const signature = req.headers['x-sendly-signature'];
259
+ const payload = req.body;
260
+
261
+ try {
262
+ const event = webhooks.verifyAndParse(payload, signature);
263
+
264
+ switch (event.type) {
265
+ case 'message.delivered':
266
+ console.log(`Message ${event.data.id} delivered`);
267
+ break;
268
+ case 'message.failed':
269
+ console.log(`Message ${event.data.id} failed: ${event.data.errorCode}`);
270
+ break;
271
+ }
272
+
273
+ res.status(200).send('OK');
274
+ } catch (error) {
275
+ console.error('Invalid signature');
276
+ res.status(400).send('Invalid signature');
277
+ }
278
+ });
279
+ ```
280
+
281
+ ## Account & Credits
282
+
283
+ ```typescript
284
+ // Get account information
285
+ const account = await sendly.account.get();
286
+ console.log(`Email: ${account.email}`);
287
+
288
+ // Check credit balance
289
+ const credits = await sendly.account.getCredits();
290
+ console.log(`Available: ${credits.availableBalance} credits`);
291
+ console.log(`Reserved (scheduled): ${credits.reservedBalance} credits`);
292
+ console.log(`Total: ${credits.balance} credits`);
293
+
294
+ // View credit transaction history
295
+ const { data: transactions } = await sendly.account.getCreditTransactions();
296
+ for (const tx of transactions) {
297
+ console.log(`${tx.type}: ${tx.amount} credits - ${tx.description}`);
298
+ }
299
+
300
+ // List API keys
301
+ const { data: keys } = await sendly.account.listApiKeys();
302
+ for (const key of keys) {
303
+ console.log(`${key.name}: ${key.prefix}*** (${key.type})`);
304
+ }
305
+
306
+ // Get API key usage stats
307
+ const usage = await sendly.account.getApiKeyUsage('key_xxx');
308
+ console.log(`Messages sent: ${usage.messagesSent}`);
309
+ console.log(`Credits used: ${usage.creditsUsed}`);
310
+ ```
311
+
150
312
  ## Error Handling
151
313
 
152
314
  The SDK provides typed error classes for different error scenarios:
@@ -201,12 +363,12 @@ console.log(sendly.isTestMode()); // true
201
363
 
202
364
  // Use sandbox test numbers
203
365
  await sendly.messages.send({
204
- to: SANDBOX_TEST_NUMBERS.SUCCESS, // +15550001234 - Always succeeds
366
+ to: SANDBOX_TEST_NUMBERS.SUCCESS, // +15005550000 - Always succeeds
205
367
  text: 'Test message'
206
368
  });
207
369
 
208
370
  await sendly.messages.send({
209
- to: SANDBOX_TEST_NUMBERS.INVALID, // +15550001001 - Returns invalid_number error
371
+ to: SANDBOX_TEST_NUMBERS.INVALID, // +15005550001 - Returns invalid_number error
210
372
  text: 'Test message'
211
373
  });
212
374
  ```
@@ -215,11 +377,12 @@ await sendly.messages.send({
215
377
 
216
378
  | Number | Behavior |
217
379
  |--------|----------|
218
- | `+15550001234` | Instant success |
219
- | `+15550001010` | Success after 10s delay |
220
- | `+15550001001` | Fails: invalid_number |
221
- | `+15550001002` | Fails: carrier_rejected (2s delay) |
222
- | `+15550001003` | Fails: rate_limit_exceeded |
380
+ | `+15005550000` | Success (instant) |
381
+ | `+15005550001` | Fails: invalid_number |
382
+ | `+15005550002` | Fails: unroutable_destination |
383
+ | `+15005550003` | Fails: queue_full |
384
+ | `+15005550004` | Fails: rate_limit_exceeded |
385
+ | `+15005550006` | Fails: carrier_violation |
223
386
 
224
387
  ## Pricing Tiers
225
388
 
@@ -297,6 +460,8 @@ new Sendly(config: SendlyConfig)
297
460
  #### Properties
298
461
 
299
462
  - `messages` - Messages resource
463
+ - `webhooks` - Webhooks resource
464
+ - `account` - Account resource
300
465
 
301
466
  #### Methods
302
467
 
@@ -318,6 +483,98 @@ List sent messages.
318
483
 
319
484
  Get a specific message by ID.
320
485
 
486
+ #### `schedule(request: ScheduleMessageRequest): Promise<ScheduledMessage>`
487
+
488
+ Schedule a message for future delivery.
489
+
490
+ #### `listScheduled(options?: ListScheduledMessagesOptions): Promise<ScheduledMessageListResponse>`
491
+
492
+ List scheduled messages.
493
+
494
+ #### `getScheduled(id: string): Promise<ScheduledMessage>`
495
+
496
+ Get a scheduled message by ID.
497
+
498
+ #### `cancelScheduled(id: string): Promise<CancelScheduledMessageResponse>`
499
+
500
+ Cancel a scheduled message and refund credits.
501
+
502
+ #### `sendBatch(request: SendBatchRequest): Promise<BatchMessageResponse>`
503
+
504
+ Send multiple messages in one API call.
505
+
506
+ #### `getBatch(batchId: string): Promise<BatchMessageResponse>`
507
+
508
+ Get batch status by ID.
509
+
510
+ #### `listBatches(options?: ListBatchesOptions): Promise<BatchListResponse>`
511
+
512
+ List all batches.
513
+
514
+ ### `sendly.webhooks`
515
+
516
+ #### `create(request: CreateWebhookRequest): Promise<Webhook>`
517
+
518
+ Create a new webhook endpoint.
519
+
520
+ #### `list(): Promise<Webhook[]>`
521
+
522
+ List all webhooks.
523
+
524
+ #### `get(id: string): Promise<Webhook>`
525
+
526
+ Get a webhook by ID.
527
+
528
+ #### `update(id: string, request: UpdateWebhookRequest): Promise<Webhook>`
529
+
530
+ Update a webhook.
531
+
532
+ #### `delete(id: string): Promise<void>`
533
+
534
+ Delete a webhook.
535
+
536
+ #### `test(id: string): Promise<WebhookTestResult>`
537
+
538
+ Send a test event to a webhook.
539
+
540
+ #### `rotateSecret(id: string): Promise<WebhookSecretRotation>`
541
+
542
+ Rotate webhook secret.
543
+
544
+ #### `getDeliveries(id: string): Promise<WebhookDelivery[]>`
545
+
546
+ Get delivery history for a webhook.
547
+
548
+ #### `retryDelivery(webhookId: string, deliveryId: string): Promise<void>`
549
+
550
+ Retry a failed delivery.
551
+
552
+ ### `sendly.account`
553
+
554
+ #### `get(): Promise<Account>`
555
+
556
+ Get account information.
557
+
558
+ #### `getCredits(): Promise<Credits>`
559
+
560
+ Get credit balance.
561
+
562
+ #### `getCreditTransactions(): Promise<CreditTransactionListResponse>`
563
+
564
+ Get credit transaction history.
565
+
566
+ #### `listApiKeys(): Promise<ApiKeyListResponse>`
567
+
568
+ List API keys.
569
+
570
+ #### `getApiKey(id: string): Promise<ApiKey>`
571
+
572
+ Get an API key by ID.
573
+
574
+ #### `getApiKeyUsage(id: string): Promise<ApiKeyUsage>`
575
+
576
+ Get usage statistics for an API key.
577
+
321
578
  ## Support
322
579
 
323
580
  - 📚 [Documentation](https://sendly.live/docs)
package/dist/index.d.mts CHANGED
@@ -755,19 +755,22 @@ interface ApiKey {
755
755
  isRevoked: boolean;
756
756
  }
757
757
  /**
758
- * Test phone numbers for sandbox mode
758
+ * Test phone numbers for sandbox mode.
759
+ * Use these with test API keys (sk_test_*) to simulate different scenarios.
759
760
  */
760
761
  declare const SANDBOX_TEST_NUMBERS: {
761
- /** Always succeeds instantly */
762
- readonly SUCCESS: "+15550001234";
763
- /** Succeeds after 10 second delay */
764
- readonly DELAYED: "+15550001010";
762
+ /** Always succeeds - any number not in error list succeeds */
763
+ readonly SUCCESS: "+15005550000";
765
764
  /** Fails with invalid_number error */
766
- readonly INVALID: "+15550001001";
767
- /** Fails with carrier_rejected error after 2 seconds */
768
- readonly REJECTED: "+15550001002";
765
+ readonly INVALID: "+15005550001";
766
+ /** Fails with unroutable destination error */
767
+ readonly UNROUTABLE: "+15005550002";
768
+ /** Fails with queue_full error */
769
+ readonly QUEUE_FULL: "+15005550003";
769
770
  /** Fails with rate_limit_exceeded error */
770
- readonly RATE_LIMITED: "+15550001003";
771
+ readonly RATE_LIMITED: "+15005550004";
772
+ /** Fails with carrier_violation error */
773
+ readonly CARRIER_VIOLATION: "+15005550006";
771
774
  };
772
775
 
773
776
  /**
package/dist/index.d.ts CHANGED
@@ -755,19 +755,22 @@ interface ApiKey {
755
755
  isRevoked: boolean;
756
756
  }
757
757
  /**
758
- * Test phone numbers for sandbox mode
758
+ * Test phone numbers for sandbox mode.
759
+ * Use these with test API keys (sk_test_*) to simulate different scenarios.
759
760
  */
760
761
  declare const SANDBOX_TEST_NUMBERS: {
761
- /** Always succeeds instantly */
762
- readonly SUCCESS: "+15550001234";
763
- /** Succeeds after 10 second delay */
764
- readonly DELAYED: "+15550001010";
762
+ /** Always succeeds - any number not in error list succeeds */
763
+ readonly SUCCESS: "+15005550000";
765
764
  /** Fails with invalid_number error */
766
- readonly INVALID: "+15550001001";
767
- /** Fails with carrier_rejected error after 2 seconds */
768
- readonly REJECTED: "+15550001002";
765
+ readonly INVALID: "+15005550001";
766
+ /** Fails with unroutable destination error */
767
+ readonly UNROUTABLE: "+15005550002";
768
+ /** Fails with queue_full error */
769
+ readonly QUEUE_FULL: "+15005550003";
769
770
  /** Fails with rate_limit_exceeded error */
770
- readonly RATE_LIMITED: "+15550001003";
771
+ readonly RATE_LIMITED: "+15005550004";
772
+ /** Fails with carrier_violation error */
773
+ readonly CARRIER_VIOLATION: "+15005550006";
771
774
  };
772
775
 
773
776
  /**
package/dist/index.js CHANGED
@@ -441,16 +441,18 @@ var SUPPORTED_COUNTRIES = {
441
441
  };
442
442
  var ALL_SUPPORTED_COUNTRIES = Object.values(SUPPORTED_COUNTRIES).flat();
443
443
  var SANDBOX_TEST_NUMBERS = {
444
- /** Always succeeds instantly */
445
- SUCCESS: "+15550001234",
446
- /** Succeeds after 10 second delay */
447
- DELAYED: "+15550001010",
444
+ /** Always succeeds - any number not in error list succeeds */
445
+ SUCCESS: "+15005550000",
448
446
  /** Fails with invalid_number error */
449
- INVALID: "+15550001001",
450
- /** Fails with carrier_rejected error after 2 seconds */
451
- REJECTED: "+15550001002",
447
+ INVALID: "+15005550001",
448
+ /** Fails with unroutable destination error */
449
+ UNROUTABLE: "+15005550002",
450
+ /** Fails with queue_full error */
451
+ QUEUE_FULL: "+15005550003",
452
452
  /** Fails with rate_limit_exceeded error */
453
- RATE_LIMITED: "+15550001003"
453
+ RATE_LIMITED: "+15005550004",
454
+ /** Fails with carrier_violation error */
455
+ CARRIER_VIOLATION: "+15005550006"
454
456
  };
455
457
 
456
458
  // src/utils/validation.ts
@@ -636,7 +638,8 @@ var MessagesResource = class {
636
638
  body: {
637
639
  to: request.to,
638
640
  text: request.text,
639
- ...request.from && { from: request.from }
641
+ ...request.from && { from: request.from },
642
+ ...request.messageType && { messageType: request.messageType }
640
643
  }
641
644
  });
642
645
  return message;
@@ -800,7 +803,8 @@ var MessagesResource = class {
800
803
  to: request.to,
801
804
  text: request.text,
802
805
  scheduledAt: request.scheduledAt,
803
- ...request.from && { from: request.from }
806
+ ...request.from && { from: request.from },
807
+ ...request.messageType && { messageType: request.messageType }
804
808
  }
805
809
  });
806
810
  return scheduled;
@@ -923,7 +927,8 @@ var MessagesResource = class {
923
927
  path: "/messages/batch",
924
928
  body: {
925
929
  messages: request.messages,
926
- ...request.from && { from: request.from }
930
+ ...request.from && { from: request.from },
931
+ ...request.messageType && { messageType: request.messageType }
927
932
  }
928
933
  });
929
934
  return batch;
package/dist/index.mjs CHANGED
@@ -381,16 +381,18 @@ var SUPPORTED_COUNTRIES = {
381
381
  };
382
382
  var ALL_SUPPORTED_COUNTRIES = Object.values(SUPPORTED_COUNTRIES).flat();
383
383
  var SANDBOX_TEST_NUMBERS = {
384
- /** Always succeeds instantly */
385
- SUCCESS: "+15550001234",
386
- /** Succeeds after 10 second delay */
387
- DELAYED: "+15550001010",
384
+ /** Always succeeds - any number not in error list succeeds */
385
+ SUCCESS: "+15005550000",
388
386
  /** Fails with invalid_number error */
389
- INVALID: "+15550001001",
390
- /** Fails with carrier_rejected error after 2 seconds */
391
- REJECTED: "+15550001002",
387
+ INVALID: "+15005550001",
388
+ /** Fails with unroutable destination error */
389
+ UNROUTABLE: "+15005550002",
390
+ /** Fails with queue_full error */
391
+ QUEUE_FULL: "+15005550003",
392
392
  /** Fails with rate_limit_exceeded error */
393
- RATE_LIMITED: "+15550001003"
393
+ RATE_LIMITED: "+15005550004",
394
+ /** Fails with carrier_violation error */
395
+ CARRIER_VIOLATION: "+15005550006"
394
396
  };
395
397
 
396
398
  // src/utils/validation.ts
@@ -576,7 +578,8 @@ var MessagesResource = class {
576
578
  body: {
577
579
  to: request.to,
578
580
  text: request.text,
579
- ...request.from && { from: request.from }
581
+ ...request.from && { from: request.from },
582
+ ...request.messageType && { messageType: request.messageType }
580
583
  }
581
584
  });
582
585
  return message;
@@ -740,7 +743,8 @@ var MessagesResource = class {
740
743
  to: request.to,
741
744
  text: request.text,
742
745
  scheduledAt: request.scheduledAt,
743
- ...request.from && { from: request.from }
746
+ ...request.from && { from: request.from },
747
+ ...request.messageType && { messageType: request.messageType }
744
748
  }
745
749
  });
746
750
  return scheduled;
@@ -863,7 +867,8 @@ var MessagesResource = class {
863
867
  path: "/messages/batch",
864
868
  body: {
865
869
  messages: request.messages,
866
- ...request.from && { from: request.from }
870
+ ...request.from && { from: request.from },
871
+ ...request.messageType && { messageType: request.messageType }
867
872
  }
868
873
  });
869
874
  return batch;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendly/node",
3
- "version": "3.5.2",
3
+ "version": "3.5.4",
4
4
  "description": "Official Sendly Node.js SDK for SMS messaging",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",