@sendly/node 3.5.4 → 3.8.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 CHANGED
@@ -168,6 +168,16 @@ const status = await sendly.messages.getBatch('batch_xxx');
168
168
 
169
169
  // List all batches
170
170
  const { data: batches } = await sendly.messages.listBatches();
171
+
172
+ // Preview batch (dry run) - validates without sending
173
+ const preview = await sendly.messages.previewBatch({
174
+ messages: [
175
+ { to: '+15551234567', text: 'Hello User 1!' },
176
+ { to: '+447700900123', text: 'Hello UK!' }
177
+ ]
178
+ });
179
+ console.log(`Total credits needed: ${preview.totalCredits}`);
180
+ console.log(`Valid: ${preview.valid}, Invalid: ${preview.invalid}`);
171
181
  ```
172
182
 
173
183
  ### Rate Limit Information
@@ -307,6 +317,17 @@ for (const key of keys) {
307
317
  const usage = await sendly.account.getApiKeyUsage('key_xxx');
308
318
  console.log(`Messages sent: ${usage.messagesSent}`);
309
319
  console.log(`Credits used: ${usage.creditsUsed}`);
320
+
321
+ // Create a new API key
322
+ const newKey = await sendly.account.createApiKey({
323
+ name: 'Production Key',
324
+ type: 'live',
325
+ scopes: ['sms:send', 'sms:read']
326
+ });
327
+ console.log(`New key: ${newKey.key}`); // Only shown once!
328
+
329
+ // Revoke an API key
330
+ await sendly.account.revokeApiKey('key_xxx');
310
331
  ```
311
332
 
312
333
  ## Error Handling
package/dist/index.d.mts CHANGED
@@ -437,6 +437,76 @@ interface BatchListResponse {
437
437
  */
438
438
  count: number;
439
439
  }
440
+ /**
441
+ * Preview result for a single message in a batch
442
+ */
443
+ interface BatchPreviewItem {
444
+ /**
445
+ * Destination phone number
446
+ */
447
+ to: string;
448
+ /**
449
+ * Whether this message will be sent
450
+ */
451
+ willSend: boolean;
452
+ /**
453
+ * Number of SMS segments
454
+ */
455
+ segments: number;
456
+ /**
457
+ * Credits required for this message
458
+ */
459
+ creditsNeeded: number;
460
+ /**
461
+ * Warning message (e.g., quiet hours)
462
+ */
463
+ warning?: string;
464
+ /**
465
+ * Block reason if willSend is false
466
+ */
467
+ blockReason?: string;
468
+ }
469
+ /**
470
+ * Response from previewing a batch (dry run)
471
+ */
472
+ interface BatchPreviewResponse {
473
+ /**
474
+ * Whether the batch can be sent
475
+ */
476
+ canSend: boolean;
477
+ /**
478
+ * Total number of messages
479
+ */
480
+ totalMessages: number;
481
+ /**
482
+ * Number of messages that will be sent
483
+ */
484
+ willSend: number;
485
+ /**
486
+ * Number of messages that will be blocked
487
+ */
488
+ blocked: number;
489
+ /**
490
+ * Total credits required
491
+ */
492
+ creditsNeeded: number;
493
+ /**
494
+ * Current credit balance
495
+ */
496
+ currentBalance: number;
497
+ /**
498
+ * Whether user has enough credits
499
+ */
500
+ hasEnoughCredits: boolean;
501
+ /**
502
+ * Per-message preview details
503
+ */
504
+ messages: BatchPreviewItem[];
505
+ /**
506
+ * Summary of why messages are blocked (if any)
507
+ */
508
+ blockReasons?: Record<string, number>;
509
+ }
440
510
  /**
441
511
  * Error codes returned by the Sendly API
442
512
  */
@@ -1082,6 +1152,29 @@ declare class MessagesResource {
1082
1152
  * ```
1083
1153
  */
1084
1154
  listBatches(options?: ListBatchesOptions): Promise<BatchListResponse>;
1155
+ /**
1156
+ * Preview a batch without sending (dry run)
1157
+ *
1158
+ * @param request - Batch request with array of messages
1159
+ * @returns Preview showing what would happen if batch was sent
1160
+ *
1161
+ * @example
1162
+ * ```typescript
1163
+ * const preview = await sendly.messages.previewBatch({
1164
+ * messages: [
1165
+ * { to: '+15551234567', text: 'Hello User 1!' },
1166
+ * { to: '+15559876543', text: 'Hello User 2!' }
1167
+ * ]
1168
+ * });
1169
+ *
1170
+ * console.log(preview.canSend); // true/false
1171
+ * console.log(preview.creditsNeeded); // 2
1172
+ * console.log(preview.hasEnoughCredits); // true/false
1173
+ * ```
1174
+ *
1175
+ * @throws {ValidationError} If any message is invalid
1176
+ */
1177
+ previewBatch(request: BatchMessageRequest): Promise<BatchPreviewResponse>;
1085
1178
  }
1086
1179
 
1087
1180
  /**
@@ -1407,6 +1500,38 @@ declare class AccountResource {
1407
1500
  periodStart: string;
1408
1501
  periodEnd: string;
1409
1502
  }>;
1503
+ /**
1504
+ * Create a new API key
1505
+ *
1506
+ * @param name - Display name for the API key
1507
+ * @param options - Optional settings
1508
+ * @returns The created API key with the full key value (only shown once)
1509
+ *
1510
+ * @example
1511
+ * ```typescript
1512
+ * const { apiKey, key } = await sendly.account.createApiKey('Production');
1513
+ * console.log(`Created key: ${key}`); // Full key - save this!
1514
+ * console.log(`Key ID: ${apiKey.id}`);
1515
+ * ```
1516
+ */
1517
+ createApiKey(name: string, options?: {
1518
+ expiresAt?: string;
1519
+ }): Promise<{
1520
+ apiKey: ApiKey;
1521
+ key: string;
1522
+ }>;
1523
+ /**
1524
+ * Revoke an API key
1525
+ *
1526
+ * @param id - API key ID to revoke
1527
+ *
1528
+ * @example
1529
+ * ```typescript
1530
+ * await sendly.account.revokeApiKey('key_xxx');
1531
+ * console.log('API key revoked');
1532
+ * ```
1533
+ */
1534
+ revokeApiKey(id: string): Promise<void>;
1410
1535
  }
1411
1536
 
1412
1537
  /**
package/dist/index.d.ts CHANGED
@@ -437,6 +437,76 @@ interface BatchListResponse {
437
437
  */
438
438
  count: number;
439
439
  }
440
+ /**
441
+ * Preview result for a single message in a batch
442
+ */
443
+ interface BatchPreviewItem {
444
+ /**
445
+ * Destination phone number
446
+ */
447
+ to: string;
448
+ /**
449
+ * Whether this message will be sent
450
+ */
451
+ willSend: boolean;
452
+ /**
453
+ * Number of SMS segments
454
+ */
455
+ segments: number;
456
+ /**
457
+ * Credits required for this message
458
+ */
459
+ creditsNeeded: number;
460
+ /**
461
+ * Warning message (e.g., quiet hours)
462
+ */
463
+ warning?: string;
464
+ /**
465
+ * Block reason if willSend is false
466
+ */
467
+ blockReason?: string;
468
+ }
469
+ /**
470
+ * Response from previewing a batch (dry run)
471
+ */
472
+ interface BatchPreviewResponse {
473
+ /**
474
+ * Whether the batch can be sent
475
+ */
476
+ canSend: boolean;
477
+ /**
478
+ * Total number of messages
479
+ */
480
+ totalMessages: number;
481
+ /**
482
+ * Number of messages that will be sent
483
+ */
484
+ willSend: number;
485
+ /**
486
+ * Number of messages that will be blocked
487
+ */
488
+ blocked: number;
489
+ /**
490
+ * Total credits required
491
+ */
492
+ creditsNeeded: number;
493
+ /**
494
+ * Current credit balance
495
+ */
496
+ currentBalance: number;
497
+ /**
498
+ * Whether user has enough credits
499
+ */
500
+ hasEnoughCredits: boolean;
501
+ /**
502
+ * Per-message preview details
503
+ */
504
+ messages: BatchPreviewItem[];
505
+ /**
506
+ * Summary of why messages are blocked (if any)
507
+ */
508
+ blockReasons?: Record<string, number>;
509
+ }
440
510
  /**
441
511
  * Error codes returned by the Sendly API
442
512
  */
@@ -1082,6 +1152,29 @@ declare class MessagesResource {
1082
1152
  * ```
1083
1153
  */
1084
1154
  listBatches(options?: ListBatchesOptions): Promise<BatchListResponse>;
1155
+ /**
1156
+ * Preview a batch without sending (dry run)
1157
+ *
1158
+ * @param request - Batch request with array of messages
1159
+ * @returns Preview showing what would happen if batch was sent
1160
+ *
1161
+ * @example
1162
+ * ```typescript
1163
+ * const preview = await sendly.messages.previewBatch({
1164
+ * messages: [
1165
+ * { to: '+15551234567', text: 'Hello User 1!' },
1166
+ * { to: '+15559876543', text: 'Hello User 2!' }
1167
+ * ]
1168
+ * });
1169
+ *
1170
+ * console.log(preview.canSend); // true/false
1171
+ * console.log(preview.creditsNeeded); // 2
1172
+ * console.log(preview.hasEnoughCredits); // true/false
1173
+ * ```
1174
+ *
1175
+ * @throws {ValidationError} If any message is invalid
1176
+ */
1177
+ previewBatch(request: BatchMessageRequest): Promise<BatchPreviewResponse>;
1085
1178
  }
1086
1179
 
1087
1180
  /**
@@ -1407,6 +1500,38 @@ declare class AccountResource {
1407
1500
  periodStart: string;
1408
1501
  periodEnd: string;
1409
1502
  }>;
1503
+ /**
1504
+ * Create a new API key
1505
+ *
1506
+ * @param name - Display name for the API key
1507
+ * @param options - Optional settings
1508
+ * @returns The created API key with the full key value (only shown once)
1509
+ *
1510
+ * @example
1511
+ * ```typescript
1512
+ * const { apiKey, key } = await sendly.account.createApiKey('Production');
1513
+ * console.log(`Created key: ${key}`); // Full key - save this!
1514
+ * console.log(`Key ID: ${apiKey.id}`);
1515
+ * ```
1516
+ */
1517
+ createApiKey(name: string, options?: {
1518
+ expiresAt?: string;
1519
+ }): Promise<{
1520
+ apiKey: ApiKey;
1521
+ key: string;
1522
+ }>;
1523
+ /**
1524
+ * Revoke an API key
1525
+ *
1526
+ * @param id - API key ID to revoke
1527
+ *
1528
+ * @example
1529
+ * ```typescript
1530
+ * await sendly.account.revokeApiKey('key_xxx');
1531
+ * console.log('API key revoked');
1532
+ * ```
1533
+ */
1534
+ revokeApiKey(id: string): Promise<void>;
1410
1535
  }
1411
1536
 
1412
1537
  /**
package/dist/index.js CHANGED
@@ -986,6 +986,53 @@ var MessagesResource = class {
986
986
  });
987
987
  return response;
988
988
  }
989
+ /**
990
+ * Preview a batch without sending (dry run)
991
+ *
992
+ * @param request - Batch request with array of messages
993
+ * @returns Preview showing what would happen if batch was sent
994
+ *
995
+ * @example
996
+ * ```typescript
997
+ * const preview = await sendly.messages.previewBatch({
998
+ * messages: [
999
+ * { to: '+15551234567', text: 'Hello User 1!' },
1000
+ * { to: '+15559876543', text: 'Hello User 2!' }
1001
+ * ]
1002
+ * });
1003
+ *
1004
+ * console.log(preview.canSend); // true/false
1005
+ * console.log(preview.creditsNeeded); // 2
1006
+ * console.log(preview.hasEnoughCredits); // true/false
1007
+ * ```
1008
+ *
1009
+ * @throws {ValidationError} If any message is invalid
1010
+ */
1011
+ async previewBatch(request) {
1012
+ if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
1013
+ throw new Error("messages must be a non-empty array");
1014
+ }
1015
+ if (request.messages.length > 1e3) {
1016
+ throw new Error("Maximum 1000 messages per batch");
1017
+ }
1018
+ for (const msg of request.messages) {
1019
+ validatePhoneNumber(msg.to);
1020
+ validateMessageText(msg.text);
1021
+ }
1022
+ if (request.from) {
1023
+ validateSenderId(request.from);
1024
+ }
1025
+ const preview = await this.http.request({
1026
+ method: "POST",
1027
+ path: "/messages/batch/preview",
1028
+ body: {
1029
+ messages: request.messages,
1030
+ ...request.from && { from: request.from },
1031
+ ...request.messageType && { messageType: request.messageType }
1032
+ }
1033
+ });
1034
+ return preview;
1035
+ }
989
1036
  };
990
1037
 
991
1038
  // src/utils/transform.ts
@@ -1425,6 +1472,54 @@ var AccountResource = class {
1425
1472
  });
1426
1473
  return usage;
1427
1474
  }
1475
+ /**
1476
+ * Create a new API key
1477
+ *
1478
+ * @param name - Display name for the API key
1479
+ * @param options - Optional settings
1480
+ * @returns The created API key with the full key value (only shown once)
1481
+ *
1482
+ * @example
1483
+ * ```typescript
1484
+ * const { apiKey, key } = await sendly.account.createApiKey('Production');
1485
+ * console.log(`Created key: ${key}`); // Full key - save this!
1486
+ * console.log(`Key ID: ${apiKey.id}`);
1487
+ * ```
1488
+ */
1489
+ async createApiKey(name, options) {
1490
+ if (!name) {
1491
+ throw new Error("API key name is required");
1492
+ }
1493
+ const response = await this.http.request({
1494
+ method: "POST",
1495
+ path: "/account/keys",
1496
+ body: {
1497
+ name,
1498
+ ...options?.expiresAt && { expiresAt: options.expiresAt }
1499
+ }
1500
+ });
1501
+ return response;
1502
+ }
1503
+ /**
1504
+ * Revoke an API key
1505
+ *
1506
+ * @param id - API key ID to revoke
1507
+ *
1508
+ * @example
1509
+ * ```typescript
1510
+ * await sendly.account.revokeApiKey('key_xxx');
1511
+ * console.log('API key revoked');
1512
+ * ```
1513
+ */
1514
+ async revokeApiKey(id) {
1515
+ if (!id) {
1516
+ throw new Error("API key ID is required");
1517
+ }
1518
+ await this.http.request({
1519
+ method: "DELETE",
1520
+ path: `/account/keys/${encodeURIComponent(id)}`
1521
+ });
1522
+ }
1428
1523
  };
1429
1524
 
1430
1525
  // src/client.ts
package/dist/index.mjs CHANGED
@@ -926,6 +926,53 @@ var MessagesResource = class {
926
926
  });
927
927
  return response;
928
928
  }
929
+ /**
930
+ * Preview a batch without sending (dry run)
931
+ *
932
+ * @param request - Batch request with array of messages
933
+ * @returns Preview showing what would happen if batch was sent
934
+ *
935
+ * @example
936
+ * ```typescript
937
+ * const preview = await sendly.messages.previewBatch({
938
+ * messages: [
939
+ * { to: '+15551234567', text: 'Hello User 1!' },
940
+ * { to: '+15559876543', text: 'Hello User 2!' }
941
+ * ]
942
+ * });
943
+ *
944
+ * console.log(preview.canSend); // true/false
945
+ * console.log(preview.creditsNeeded); // 2
946
+ * console.log(preview.hasEnoughCredits); // true/false
947
+ * ```
948
+ *
949
+ * @throws {ValidationError} If any message is invalid
950
+ */
951
+ async previewBatch(request) {
952
+ if (!request.messages || !Array.isArray(request.messages) || request.messages.length === 0) {
953
+ throw new Error("messages must be a non-empty array");
954
+ }
955
+ if (request.messages.length > 1e3) {
956
+ throw new Error("Maximum 1000 messages per batch");
957
+ }
958
+ for (const msg of request.messages) {
959
+ validatePhoneNumber(msg.to);
960
+ validateMessageText(msg.text);
961
+ }
962
+ if (request.from) {
963
+ validateSenderId(request.from);
964
+ }
965
+ const preview = await this.http.request({
966
+ method: "POST",
967
+ path: "/messages/batch/preview",
968
+ body: {
969
+ messages: request.messages,
970
+ ...request.from && { from: request.from },
971
+ ...request.messageType && { messageType: request.messageType }
972
+ }
973
+ });
974
+ return preview;
975
+ }
929
976
  };
930
977
 
931
978
  // src/utils/transform.ts
@@ -1365,6 +1412,54 @@ var AccountResource = class {
1365
1412
  });
1366
1413
  return usage;
1367
1414
  }
1415
+ /**
1416
+ * Create a new API key
1417
+ *
1418
+ * @param name - Display name for the API key
1419
+ * @param options - Optional settings
1420
+ * @returns The created API key with the full key value (only shown once)
1421
+ *
1422
+ * @example
1423
+ * ```typescript
1424
+ * const { apiKey, key } = await sendly.account.createApiKey('Production');
1425
+ * console.log(`Created key: ${key}`); // Full key - save this!
1426
+ * console.log(`Key ID: ${apiKey.id}`);
1427
+ * ```
1428
+ */
1429
+ async createApiKey(name, options) {
1430
+ if (!name) {
1431
+ throw new Error("API key name is required");
1432
+ }
1433
+ const response = await this.http.request({
1434
+ method: "POST",
1435
+ path: "/account/keys",
1436
+ body: {
1437
+ name,
1438
+ ...options?.expiresAt && { expiresAt: options.expiresAt }
1439
+ }
1440
+ });
1441
+ return response;
1442
+ }
1443
+ /**
1444
+ * Revoke an API key
1445
+ *
1446
+ * @param id - API key ID to revoke
1447
+ *
1448
+ * @example
1449
+ * ```typescript
1450
+ * await sendly.account.revokeApiKey('key_xxx');
1451
+ * console.log('API key revoked');
1452
+ * ```
1453
+ */
1454
+ async revokeApiKey(id) {
1455
+ if (!id) {
1456
+ throw new Error("API key ID is required");
1457
+ }
1458
+ await this.http.request({
1459
+ method: "DELETE",
1460
+ path: `/account/keys/${encodeURIComponent(id)}`
1461
+ });
1462
+ }
1368
1463
  };
1369
1464
 
1370
1465
  // src/client.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendly/node",
3
- "version": "3.5.4",
3
+ "version": "3.8.0",
4
4
  "description": "Official Sendly Node.js SDK for SMS messaging",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",