@sendly/node 3.29.0 → 3.31.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
@@ -185,8 +185,8 @@ const preview = await sendly.messages.previewBatch({
185
185
  { to: '+447700900123', text: 'Hello UK!' }
186
186
  ]
187
187
  });
188
- console.log(`Total credits needed: ${preview.totalCredits}`);
189
- console.log(`Valid: ${preview.valid}, Invalid: ${preview.invalid}`);
188
+ console.log(`Credits needed: ${preview.creditsNeeded}`);
189
+ console.log(`Will send: ${preview.willSend}, Blocked: ${preview.blocked}`);
190
190
  ```
191
191
 
192
192
  ### Rate Limit Information
@@ -233,7 +233,7 @@ const webhook = await sendly.webhooks.create({
233
233
  });
234
234
 
235
235
  console.log(`Webhook ID: ${webhook.id}`);
236
- console.log(`Secret: ${webhook.secret}`); // Store this securely!
236
+ console.log(`Secret: ${webhook.secret}`); // Only returned at creation - store securely!
237
237
 
238
238
  // List all webhooks
239
239
  const webhooks = await sendly.webhooks.list();
@@ -275,10 +275,11 @@ const webhooks = new Webhooks('your_webhook_secret');
275
275
  // In your webhook handler
276
276
  app.post('/webhooks/sendly', (req, res) => {
277
277
  const signature = req.headers['x-sendly-signature'];
278
+ const timestamp = req.headers['x-sendly-timestamp'];
278
279
  const payload = req.body;
279
280
 
280
281
  try {
281
- const event = webhooks.verifyAndParse(payload, signature);
282
+ const event = webhooks.parse(payload, signature, timestamp);
282
283
 
283
284
  switch (event.type) {
284
285
  case 'message.delivered':
@@ -311,13 +312,13 @@ console.log(`Reserved (scheduled): ${credits.reservedBalance} credits`);
311
312
  console.log(`Total: ${credits.balance} credits`);
312
313
 
313
314
  // View credit transaction history
314
- const { data: transactions } = await sendly.account.getCreditTransactions();
315
+ const transactions = await sendly.account.getCreditTransactions();
315
316
  for (const tx of transactions) {
316
317
  console.log(`${tx.type}: ${tx.amount} credits - ${tx.description}`);
317
318
  }
318
319
 
319
320
  // List API keys
320
- const { data: keys } = await sendly.account.listApiKeys();
321
+ const keys = await sendly.account.listApiKeys();
321
322
  for (const key of keys) {
322
323
  console.log(`${key.name}: ${key.prefix}*** (${key.type})`);
323
324
  }
@@ -328,12 +329,9 @@ console.log(`Messages sent: ${usage.messagesSent}`);
328
329
  console.log(`Credits used: ${usage.creditsUsed}`);
329
330
 
330
331
  // Create a new API key
331
- const newKey = await sendly.account.createApiKey({
332
- name: 'Production Key',
333
- type: 'live',
334
- scopes: ['sms:send', 'sms:read']
335
- });
336
- console.log(`New key: ${newKey.key}`); // Only shown once!
332
+ const { apiKey, key } = await sendly.account.createApiKey('Production Key');
333
+ console.log(`New key: ${key}`); // Only shown once!
334
+ console.log(`Key ID: ${apiKey.id}`);
337
335
 
338
336
  // Revoke an API key
339
337
  await sendly.account.revokeApiKey('key_xxx');
@@ -525,11 +523,11 @@ List scheduled messages.
525
523
 
526
524
  Get a scheduled message by ID.
527
525
 
528
- #### `cancelScheduled(id: string): Promise<CancelScheduledMessageResponse>`
526
+ #### `cancelScheduled(id: string): Promise<CancelledMessageResponse>`
529
527
 
530
528
  Cancel a scheduled message and refund credits.
531
529
 
532
- #### `sendBatch(request: SendBatchRequest): Promise<BatchMessageResponse>`
530
+ #### `sendBatch(request: BatchMessageRequest): Promise<BatchMessageResponse>`
533
531
 
534
532
  Send multiple messages in one API call.
535
533
 
@@ -543,9 +541,9 @@ List all batches.
543
541
 
544
542
  ### `sendly.webhooks`
545
543
 
546
- #### `create(request: CreateWebhookRequest): Promise<Webhook>`
544
+ #### `create(options: CreateWebhookOptions): Promise<WebhookCreatedResponse>`
547
545
 
548
- Create a new webhook endpoint.
546
+ Create a new webhook endpoint. The returned object includes a one-time `secret`.
549
547
 
550
548
  #### `list(): Promise<Webhook[]>`
551
549
 
@@ -555,7 +553,7 @@ List all webhooks.
555
553
 
556
554
  Get a webhook by ID.
557
555
 
558
- #### `update(id: string, request: UpdateWebhookRequest): Promise<Webhook>`
556
+ #### `update(id: string, options: UpdateWebhookOptions): Promise<Webhook>`
559
557
 
560
558
  Update a webhook.
561
559
 
@@ -589,11 +587,11 @@ Get account information.
589
587
 
590
588
  Get credit balance.
591
589
 
592
- #### `getCreditTransactions(): Promise<CreditTransactionListResponse>`
590
+ #### `getCreditTransactions(options?: { limit?: number; offset?: number }): Promise<CreditTransaction[]>`
593
591
 
594
592
  Get credit transaction history.
595
593
 
596
- #### `listApiKeys(): Promise<ApiKeyListResponse>`
594
+ #### `listApiKeys(): Promise<ApiKey[]>`
597
595
 
598
596
  List API keys.
599
597
 
@@ -662,25 +660,27 @@ await client.enterprise.workspaces.delete('ws_xxx');
662
660
  // Submit full verification
663
661
  await client.enterprise.workspaces.submitVerification('ws_xxx', {
664
662
  businessName: 'Acme Insurance LLC',
665
- businessType: 'llc',
666
- ein: '12-3456789',
667
- address: '100 Main St',
668
- city: 'Austin',
669
- state: 'TX',
670
- zip: '78701',
663
+ website: 'https://acme.com',
664
+ entityType: 'PRIVATE_PROFIT',
665
+ brn: '12-3456789',
666
+ brnType: 'EIN',
667
+ brnCountry: 'US',
668
+ address: { street: '100 Main St', city: 'Austin', state: 'TX', zip: '78701', country: 'US' },
669
+ contact: { firstName: 'Jane', lastName: 'Doe', email: 'jane@acme.com', phone: '+15551234567' },
671
670
  useCase: 'Policy renewal reminders',
672
- sampleMessages: ['Your policy renews on 3/15.']
671
+ sampleMessages: 'Your policy renews on 3/15.'
673
672
  });
674
673
 
675
- // Inherit from verified workspace
674
+ // Inherit from verified workspace (shares toll-free number)
676
675
  await client.enterprise.workspaces.inheritVerification('ws_new', {
677
676
  sourceWorkspaceId: 'ws_verified'
678
677
  });
679
678
 
680
- // Inherit with new number
681
- await client.enterprise.workspaces.inheritVerification('ws_new', {
679
+ // Inherit + new number: use provision() with inheritWithNewNumber instead
680
+ await client.enterprise.provision({
681
+ name: 'Acme Insurance - Austin',
682
682
  sourceWorkspaceId: 'ws_verified',
683
- purchaseNewNumber: true
683
+ inheritWithNewNumber: true
684
684
  });
685
685
  ```
686
686
 
package/dist/index.d.mts CHANGED
@@ -978,6 +978,72 @@ interface UpdateWebhookOptions {
978
978
  /** Custom metadata */
979
979
  metadata?: Record<string, unknown>;
980
980
  }
981
+ /**
982
+ * Options for replaying webhook deliveries
983
+ */
984
+ interface WebhookRedeliverOptions {
985
+ /** Earliest delivery created_at to consider, ISO-8601 (default: now − 24h) */
986
+ since?: string;
987
+ /** Latest delivery created_at to consider, ISO-8601 (default: now) */
988
+ until?: string;
989
+ /** Filter by event type (default: all) */
990
+ eventTypes?: WebhookEventType[];
991
+ /** Replay deliveries in any of these statuses (default: ['failed', 'cancelled']) */
992
+ statuses?: DeliveryStatus[];
993
+ /** Maximum number of deliveries to requeue (default: 1000, max 10000) */
994
+ limit?: number;
995
+ }
996
+ /**
997
+ * Result of replaying webhook deliveries
998
+ */
999
+ interface WebhookRedeliverResult {
1000
+ message: string;
1001
+ /** Number of deliveries that were re-queued */
1002
+ requeued: number;
1003
+ /** Number of deliveries that failed to re-queue */
1004
+ skipped: number;
1005
+ /** True if the matching set was larger than `limit` */
1006
+ truncated: boolean;
1007
+ /** Total number of matching deliveries before the limit was applied */
1008
+ windowSize: number;
1009
+ /** IDs of the new delivery records created by the replay */
1010
+ deliveryIds: string[];
1011
+ since: string;
1012
+ until: string;
1013
+ limit: number;
1014
+ }
1015
+ /**
1016
+ * Options for backfilling missed webhook deliveries from the message log
1017
+ */
1018
+ interface WebhookBackfillOptions {
1019
+ /** Earliest message created_at to consider, ISO-8601 (default: now − 24h) */
1020
+ since?: string;
1021
+ /** Latest message created_at to consider, ISO-8601 (default: now) */
1022
+ until?: string;
1023
+ /** Filter by event type (default: subscribed message events) */
1024
+ eventTypes?: WebhookEventType[];
1025
+ /** Maximum number of events to synthesize (default: 1000, max 10000) */
1026
+ limit?: number;
1027
+ }
1028
+ /**
1029
+ * Result of backfilling missed webhook deliveries
1030
+ */
1031
+ interface WebhookBackfillResult {
1032
+ message: string;
1033
+ /** Number of deliveries synthesized and dispatched */
1034
+ synthesized: number;
1035
+ /** Synthesized count grouped by event type */
1036
+ byType: Record<string, number>;
1037
+ /** True if there were more eligible events than `limit` */
1038
+ truncated: boolean;
1039
+ /** Total number of messages scanned for the window */
1040
+ candidatesScanned: number;
1041
+ /** IDs of the new delivery records */
1042
+ deliveryIds: string[];
1043
+ since: string;
1044
+ until: string;
1045
+ limit: number;
1046
+ }
981
1047
  /**
982
1048
  * A webhook delivery attempt
983
1049
  */
@@ -2651,6 +2717,62 @@ declare class WebhooksResource {
2651
2717
  message: string;
2652
2718
  webhook: Webhook;
2653
2719
  }>;
2720
+ /**
2721
+ * Replay failed or cancelled webhook deliveries from the audit log.
2722
+ *
2723
+ * Use after a customer endpoint has recovered from an outage to re-fire
2724
+ * deliveries that we recorded but couldn't deliver. Each replay creates
2725
+ * a new delivery row preserving the original `event_id` so customers can
2726
+ * dedupe.
2727
+ *
2728
+ * Rejects with HTTP 409 if the circuit is currently open — call
2729
+ * {@link WebhooksResource.resetCircuit} first.
2730
+ *
2731
+ * @param id - Webhook ID
2732
+ * @param options - Window and filter options
2733
+ * @returns Counts of requeued deliveries plus the new delivery IDs
2734
+ *
2735
+ * @example
2736
+ * ```typescript
2737
+ * await sendly.webhooks.resetCircuit('whk_xxx');
2738
+ * const result = await sendly.webhooks.redeliver('whk_xxx', {
2739
+ * since: '2026-05-01T00:00:00Z',
2740
+ * eventTypes: ['message.delivered', 'message.failed'],
2741
+ * limit: 5000,
2742
+ * });
2743
+ * console.log(`Requeued ${result.requeued} deliveries`);
2744
+ * ```
2745
+ */
2746
+ redeliver(id: string, options?: WebhookRedeliverOptions): Promise<WebhookRedeliverResult>;
2747
+ /**
2748
+ * Backfill missed webhook events from the underlying message log.
2749
+ *
2750
+ * Use this when a circuit-breaker outage left events with no audit row
2751
+ * (the case `redeliver` cannot recover). The endpoint scans the
2752
+ * `messages` table for the window and synthesizes a webhook delivery
2753
+ * for any message whose `message.sent` / `message.delivered` /
2754
+ * `message.failed` event has not been successfully delivered yet.
2755
+ *
2756
+ * Synthesized events have fresh IDs — your endpoint should dedupe by
2757
+ * `event.data.object.id` (the message ID).
2758
+ *
2759
+ * Rejects with HTTP 409 if the circuit is currently open — call
2760
+ * {@link WebhooksResource.resetCircuit} first.
2761
+ *
2762
+ * @param id - Webhook ID
2763
+ * @param options - Window and filter options
2764
+ * @returns Counts grouped by event type plus the new delivery IDs
2765
+ *
2766
+ * @example
2767
+ * ```typescript
2768
+ * const result = await sendly.webhooks.backfill('whk_xxx', {
2769
+ * since: '2026-05-01T00:00:00Z',
2770
+ * eventTypes: ['message.delivered', 'message.failed'],
2771
+ * });
2772
+ * console.log(`Synthesized ${result.synthesized} events`, result.byType);
2773
+ * ```
2774
+ */
2775
+ backfill(id: string, options?: WebhookBackfillOptions): Promise<WebhookBackfillResult>;
2654
2776
  /**
2655
2777
  * Rotate the webhook signing secret
2656
2778
  *
@@ -3773,6 +3895,49 @@ declare class MediaResource {
3773
3895
  upload(file: Buffer | NodeJS.ReadableStream, options?: MediaUploadOptions): Promise<MediaFile>;
3774
3896
  }
3775
3897
 
3898
+ /**
3899
+ * Verification submit/resubmit payload. All fields optional for resubmits
3900
+ * (server merges with existing record). For initial provision via
3901
+ * `submitVerification` (no existing record), the validator requires:
3902
+ * businessName, website, address, contact, useCase, useCaseSummary,
3903
+ * sampleMessages, optInWorkflow.
3904
+ */
3905
+ interface VerificationSubmitInput {
3906
+ businessName?: string;
3907
+ doingBusinessAs?: string;
3908
+ website?: string;
3909
+ address?: {
3910
+ street?: string;
3911
+ address1?: string;
3912
+ address2?: string | null;
3913
+ city?: string;
3914
+ state?: string;
3915
+ zip?: string;
3916
+ country?: string;
3917
+ };
3918
+ contact?: {
3919
+ firstName?: string;
3920
+ lastName?: string;
3921
+ email?: string;
3922
+ phone?: string;
3923
+ };
3924
+ brn?: string | null;
3925
+ brnType?: "EIN" | "SSN" | "DUNS" | "CRA" | "VAT" | "LEI" | "OTHER" | null;
3926
+ brnCountry?: string | null;
3927
+ entityType?: "SOLE_PROPRIETOR" | "PRIVATE_PROFIT" | "PUBLIC_PROFIT" | "NON_PROFIT" | "GOVERNMENT";
3928
+ useCase?: string;
3929
+ useCaseSummary?: string;
3930
+ sampleMessages?: string;
3931
+ optInWorkflow?: string;
3932
+ optInImageUrls?: string;
3933
+ monthlyVolume?: string;
3934
+ additionalInformation?: string;
3935
+ ageGatedContent?: boolean;
3936
+ isvReseller?: string;
3937
+ privacyUrl?: string;
3938
+ termsUrl?: string;
3939
+ }
3940
+
3776
3941
  declare class WorkspacesSubResource {
3777
3942
  private readonly http;
3778
3943
  constructor(http: HttpClient);
@@ -3797,18 +3962,46 @@ declare class WorkspacesSubResource {
3797
3962
  }>;
3798
3963
  get(workspaceId: string): Promise<EnterpriseWorkspaceDetail>;
3799
3964
  delete(workspaceId: string): Promise<void>;
3800
- submitVerification(workspaceId: string, data: {
3801
- businessName: string;
3802
- businessType: string;
3803
- ein: string;
3804
- address: string;
3805
- city: string;
3806
- state: string;
3807
- zip: string;
3808
- useCase: string;
3809
- sampleMessages: string[];
3810
- monthlyVolume?: number;
3811
- }): Promise<unknown>;
3965
+ /**
3966
+ * Submit (or resubmit) a verification for an enterprise workspace.
3967
+ *
3968
+ * Partial-update friendly (May 2026): for resubmit on an existing
3969
+ * workspace, you only need to send the fields you want to change —
3970
+ * everything else is preserved from the existing record. Hosted page
3971
+ * URLs (`/biz/`, `/opt-in/`, `/legal/`) generated during provision
3972
+ * are auto-preserved.
3973
+ *
3974
+ * For sole proprietors, leave `brn`, `brnType`, `brnCountry` undefined
3975
+ * — the server strips them before forwarding to the carrier.
3976
+ *
3977
+ * @example Full submit
3978
+ * ```ts
3979
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3980
+ * businessName: "Acme LLC",
3981
+ * website: "https://acme.com",
3982
+ * address: { street: "...", city: "...", state: "California", zip: "90001", country: "US" },
3983
+ * contact: { firstName: "...", lastName: "...", email: "...", phone: "+15551234567" },
3984
+ * useCase: "Insurance Services",
3985
+ * useCaseSummary: "...",
3986
+ * sampleMessages: "...",
3987
+ * optInWorkflow: "...",
3988
+ * entityType: "SOLE_PROPRIETOR",
3989
+ * });
3990
+ * ```
3991
+ *
3992
+ * @example Partial-update resubmit (only changing email)
3993
+ * ```ts
3994
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3995
+ * contact: { email: "new@email.com" },
3996
+ * });
3997
+ * ```
3998
+ */
3999
+ submitVerification(workspaceId: string, data: VerificationSubmitInput): Promise<unknown>;
4000
+ /**
4001
+ * Convenience alias for resubmits. Reads more naturally when you only
4002
+ * want to update a few fields after a rejection.
4003
+ */
4004
+ resubmitVerification(workspaceId: string, partialUpdates: VerificationSubmitInput): Promise<unknown>;
3812
4005
  inheritVerification(workspaceId: string, options: {
3813
4006
  sourceWorkspaceId: string;
3814
4007
  }): Promise<unknown>;
package/dist/index.d.ts CHANGED
@@ -978,6 +978,72 @@ interface UpdateWebhookOptions {
978
978
  /** Custom metadata */
979
979
  metadata?: Record<string, unknown>;
980
980
  }
981
+ /**
982
+ * Options for replaying webhook deliveries
983
+ */
984
+ interface WebhookRedeliverOptions {
985
+ /** Earliest delivery created_at to consider, ISO-8601 (default: now − 24h) */
986
+ since?: string;
987
+ /** Latest delivery created_at to consider, ISO-8601 (default: now) */
988
+ until?: string;
989
+ /** Filter by event type (default: all) */
990
+ eventTypes?: WebhookEventType[];
991
+ /** Replay deliveries in any of these statuses (default: ['failed', 'cancelled']) */
992
+ statuses?: DeliveryStatus[];
993
+ /** Maximum number of deliveries to requeue (default: 1000, max 10000) */
994
+ limit?: number;
995
+ }
996
+ /**
997
+ * Result of replaying webhook deliveries
998
+ */
999
+ interface WebhookRedeliverResult {
1000
+ message: string;
1001
+ /** Number of deliveries that were re-queued */
1002
+ requeued: number;
1003
+ /** Number of deliveries that failed to re-queue */
1004
+ skipped: number;
1005
+ /** True if the matching set was larger than `limit` */
1006
+ truncated: boolean;
1007
+ /** Total number of matching deliveries before the limit was applied */
1008
+ windowSize: number;
1009
+ /** IDs of the new delivery records created by the replay */
1010
+ deliveryIds: string[];
1011
+ since: string;
1012
+ until: string;
1013
+ limit: number;
1014
+ }
1015
+ /**
1016
+ * Options for backfilling missed webhook deliveries from the message log
1017
+ */
1018
+ interface WebhookBackfillOptions {
1019
+ /** Earliest message created_at to consider, ISO-8601 (default: now − 24h) */
1020
+ since?: string;
1021
+ /** Latest message created_at to consider, ISO-8601 (default: now) */
1022
+ until?: string;
1023
+ /** Filter by event type (default: subscribed message events) */
1024
+ eventTypes?: WebhookEventType[];
1025
+ /** Maximum number of events to synthesize (default: 1000, max 10000) */
1026
+ limit?: number;
1027
+ }
1028
+ /**
1029
+ * Result of backfilling missed webhook deliveries
1030
+ */
1031
+ interface WebhookBackfillResult {
1032
+ message: string;
1033
+ /** Number of deliveries synthesized and dispatched */
1034
+ synthesized: number;
1035
+ /** Synthesized count grouped by event type */
1036
+ byType: Record<string, number>;
1037
+ /** True if there were more eligible events than `limit` */
1038
+ truncated: boolean;
1039
+ /** Total number of messages scanned for the window */
1040
+ candidatesScanned: number;
1041
+ /** IDs of the new delivery records */
1042
+ deliveryIds: string[];
1043
+ since: string;
1044
+ until: string;
1045
+ limit: number;
1046
+ }
981
1047
  /**
982
1048
  * A webhook delivery attempt
983
1049
  */
@@ -2651,6 +2717,62 @@ declare class WebhooksResource {
2651
2717
  message: string;
2652
2718
  webhook: Webhook;
2653
2719
  }>;
2720
+ /**
2721
+ * Replay failed or cancelled webhook deliveries from the audit log.
2722
+ *
2723
+ * Use after a customer endpoint has recovered from an outage to re-fire
2724
+ * deliveries that we recorded but couldn't deliver. Each replay creates
2725
+ * a new delivery row preserving the original `event_id` so customers can
2726
+ * dedupe.
2727
+ *
2728
+ * Rejects with HTTP 409 if the circuit is currently open — call
2729
+ * {@link WebhooksResource.resetCircuit} first.
2730
+ *
2731
+ * @param id - Webhook ID
2732
+ * @param options - Window and filter options
2733
+ * @returns Counts of requeued deliveries plus the new delivery IDs
2734
+ *
2735
+ * @example
2736
+ * ```typescript
2737
+ * await sendly.webhooks.resetCircuit('whk_xxx');
2738
+ * const result = await sendly.webhooks.redeliver('whk_xxx', {
2739
+ * since: '2026-05-01T00:00:00Z',
2740
+ * eventTypes: ['message.delivered', 'message.failed'],
2741
+ * limit: 5000,
2742
+ * });
2743
+ * console.log(`Requeued ${result.requeued} deliveries`);
2744
+ * ```
2745
+ */
2746
+ redeliver(id: string, options?: WebhookRedeliverOptions): Promise<WebhookRedeliverResult>;
2747
+ /**
2748
+ * Backfill missed webhook events from the underlying message log.
2749
+ *
2750
+ * Use this when a circuit-breaker outage left events with no audit row
2751
+ * (the case `redeliver` cannot recover). The endpoint scans the
2752
+ * `messages` table for the window and synthesizes a webhook delivery
2753
+ * for any message whose `message.sent` / `message.delivered` /
2754
+ * `message.failed` event has not been successfully delivered yet.
2755
+ *
2756
+ * Synthesized events have fresh IDs — your endpoint should dedupe by
2757
+ * `event.data.object.id` (the message ID).
2758
+ *
2759
+ * Rejects with HTTP 409 if the circuit is currently open — call
2760
+ * {@link WebhooksResource.resetCircuit} first.
2761
+ *
2762
+ * @param id - Webhook ID
2763
+ * @param options - Window and filter options
2764
+ * @returns Counts grouped by event type plus the new delivery IDs
2765
+ *
2766
+ * @example
2767
+ * ```typescript
2768
+ * const result = await sendly.webhooks.backfill('whk_xxx', {
2769
+ * since: '2026-05-01T00:00:00Z',
2770
+ * eventTypes: ['message.delivered', 'message.failed'],
2771
+ * });
2772
+ * console.log(`Synthesized ${result.synthesized} events`, result.byType);
2773
+ * ```
2774
+ */
2775
+ backfill(id: string, options?: WebhookBackfillOptions): Promise<WebhookBackfillResult>;
2654
2776
  /**
2655
2777
  * Rotate the webhook signing secret
2656
2778
  *
@@ -3773,6 +3895,49 @@ declare class MediaResource {
3773
3895
  upload(file: Buffer | NodeJS.ReadableStream, options?: MediaUploadOptions): Promise<MediaFile>;
3774
3896
  }
3775
3897
 
3898
+ /**
3899
+ * Verification submit/resubmit payload. All fields optional for resubmits
3900
+ * (server merges with existing record). For initial provision via
3901
+ * `submitVerification` (no existing record), the validator requires:
3902
+ * businessName, website, address, contact, useCase, useCaseSummary,
3903
+ * sampleMessages, optInWorkflow.
3904
+ */
3905
+ interface VerificationSubmitInput {
3906
+ businessName?: string;
3907
+ doingBusinessAs?: string;
3908
+ website?: string;
3909
+ address?: {
3910
+ street?: string;
3911
+ address1?: string;
3912
+ address2?: string | null;
3913
+ city?: string;
3914
+ state?: string;
3915
+ zip?: string;
3916
+ country?: string;
3917
+ };
3918
+ contact?: {
3919
+ firstName?: string;
3920
+ lastName?: string;
3921
+ email?: string;
3922
+ phone?: string;
3923
+ };
3924
+ brn?: string | null;
3925
+ brnType?: "EIN" | "SSN" | "DUNS" | "CRA" | "VAT" | "LEI" | "OTHER" | null;
3926
+ brnCountry?: string | null;
3927
+ entityType?: "SOLE_PROPRIETOR" | "PRIVATE_PROFIT" | "PUBLIC_PROFIT" | "NON_PROFIT" | "GOVERNMENT";
3928
+ useCase?: string;
3929
+ useCaseSummary?: string;
3930
+ sampleMessages?: string;
3931
+ optInWorkflow?: string;
3932
+ optInImageUrls?: string;
3933
+ monthlyVolume?: string;
3934
+ additionalInformation?: string;
3935
+ ageGatedContent?: boolean;
3936
+ isvReseller?: string;
3937
+ privacyUrl?: string;
3938
+ termsUrl?: string;
3939
+ }
3940
+
3776
3941
  declare class WorkspacesSubResource {
3777
3942
  private readonly http;
3778
3943
  constructor(http: HttpClient);
@@ -3797,18 +3962,46 @@ declare class WorkspacesSubResource {
3797
3962
  }>;
3798
3963
  get(workspaceId: string): Promise<EnterpriseWorkspaceDetail>;
3799
3964
  delete(workspaceId: string): Promise<void>;
3800
- submitVerification(workspaceId: string, data: {
3801
- businessName: string;
3802
- businessType: string;
3803
- ein: string;
3804
- address: string;
3805
- city: string;
3806
- state: string;
3807
- zip: string;
3808
- useCase: string;
3809
- sampleMessages: string[];
3810
- monthlyVolume?: number;
3811
- }): Promise<unknown>;
3965
+ /**
3966
+ * Submit (or resubmit) a verification for an enterprise workspace.
3967
+ *
3968
+ * Partial-update friendly (May 2026): for resubmit on an existing
3969
+ * workspace, you only need to send the fields you want to change —
3970
+ * everything else is preserved from the existing record. Hosted page
3971
+ * URLs (`/biz/`, `/opt-in/`, `/legal/`) generated during provision
3972
+ * are auto-preserved.
3973
+ *
3974
+ * For sole proprietors, leave `brn`, `brnType`, `brnCountry` undefined
3975
+ * — the server strips them before forwarding to the carrier.
3976
+ *
3977
+ * @example Full submit
3978
+ * ```ts
3979
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3980
+ * businessName: "Acme LLC",
3981
+ * website: "https://acme.com",
3982
+ * address: { street: "...", city: "...", state: "California", zip: "90001", country: "US" },
3983
+ * contact: { firstName: "...", lastName: "...", email: "...", phone: "+15551234567" },
3984
+ * useCase: "Insurance Services",
3985
+ * useCaseSummary: "...",
3986
+ * sampleMessages: "...",
3987
+ * optInWorkflow: "...",
3988
+ * entityType: "SOLE_PROPRIETOR",
3989
+ * });
3990
+ * ```
3991
+ *
3992
+ * @example Partial-update resubmit (only changing email)
3993
+ * ```ts
3994
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3995
+ * contact: { email: "new@email.com" },
3996
+ * });
3997
+ * ```
3998
+ */
3999
+ submitVerification(workspaceId: string, data: VerificationSubmitInput): Promise<unknown>;
4000
+ /**
4001
+ * Convenience alias for resubmits. Reads more naturally when you only
4002
+ * want to update a few fields after a rejection.
4003
+ */
4004
+ resubmitVerification(workspaceId: string, partialUpdates: VerificationSubmitInput): Promise<unknown>;
3812
4005
  inheritVerification(workspaceId: string, options: {
3813
4006
  sourceWorkspaceId: string;
3814
4007
  }): Promise<unknown>;
package/dist/index.js CHANGED
@@ -1316,6 +1316,93 @@ var WebhooksResource = class {
1316
1316
  });
1317
1317
  return transformKeys(response);
1318
1318
  }
1319
+ /**
1320
+ * Replay failed or cancelled webhook deliveries from the audit log.
1321
+ *
1322
+ * Use after a customer endpoint has recovered from an outage to re-fire
1323
+ * deliveries that we recorded but couldn't deliver. Each replay creates
1324
+ * a new delivery row preserving the original `event_id` so customers can
1325
+ * dedupe.
1326
+ *
1327
+ * Rejects with HTTP 409 if the circuit is currently open — call
1328
+ * {@link WebhooksResource.resetCircuit} first.
1329
+ *
1330
+ * @param id - Webhook ID
1331
+ * @param options - Window and filter options
1332
+ * @returns Counts of requeued deliveries plus the new delivery IDs
1333
+ *
1334
+ * @example
1335
+ * ```typescript
1336
+ * await sendly.webhooks.resetCircuit('whk_xxx');
1337
+ * const result = await sendly.webhooks.redeliver('whk_xxx', {
1338
+ * since: '2026-05-01T00:00:00Z',
1339
+ * eventTypes: ['message.delivered', 'message.failed'],
1340
+ * limit: 5000,
1341
+ * });
1342
+ * console.log(`Requeued ${result.requeued} deliveries`);
1343
+ * ```
1344
+ */
1345
+ async redeliver(id, options = {}) {
1346
+ if (!id || !id.startsWith("whk_")) {
1347
+ throw new Error("Invalid webhook ID format");
1348
+ }
1349
+ const body = {};
1350
+ if (options.since !== void 0) body.since = options.since;
1351
+ if (options.until !== void 0) body.until = options.until;
1352
+ if (options.eventTypes !== void 0) body.event_types = options.eventTypes;
1353
+ if (options.statuses !== void 0) body.statuses = options.statuses;
1354
+ if (options.limit !== void 0) body.limit = options.limit;
1355
+ const response = await this.http.request({
1356
+ method: "POST",
1357
+ path: `/webhooks/${encodeURIComponent(id)}/redeliver`,
1358
+ body
1359
+ });
1360
+ return transformKeys(response);
1361
+ }
1362
+ /**
1363
+ * Backfill missed webhook events from the underlying message log.
1364
+ *
1365
+ * Use this when a circuit-breaker outage left events with no audit row
1366
+ * (the case `redeliver` cannot recover). The endpoint scans the
1367
+ * `messages` table for the window and synthesizes a webhook delivery
1368
+ * for any message whose `message.sent` / `message.delivered` /
1369
+ * `message.failed` event has not been successfully delivered yet.
1370
+ *
1371
+ * Synthesized events have fresh IDs — your endpoint should dedupe by
1372
+ * `event.data.object.id` (the message ID).
1373
+ *
1374
+ * Rejects with HTTP 409 if the circuit is currently open — call
1375
+ * {@link WebhooksResource.resetCircuit} first.
1376
+ *
1377
+ * @param id - Webhook ID
1378
+ * @param options - Window and filter options
1379
+ * @returns Counts grouped by event type plus the new delivery IDs
1380
+ *
1381
+ * @example
1382
+ * ```typescript
1383
+ * const result = await sendly.webhooks.backfill('whk_xxx', {
1384
+ * since: '2026-05-01T00:00:00Z',
1385
+ * eventTypes: ['message.delivered', 'message.failed'],
1386
+ * });
1387
+ * console.log(`Synthesized ${result.synthesized} events`, result.byType);
1388
+ * ```
1389
+ */
1390
+ async backfill(id, options = {}) {
1391
+ if (!id || !id.startsWith("whk_")) {
1392
+ throw new Error("Invalid webhook ID format");
1393
+ }
1394
+ const body = {};
1395
+ if (options.since !== void 0) body.since = options.since;
1396
+ if (options.until !== void 0) body.until = options.until;
1397
+ if (options.eventTypes !== void 0) body.event_types = options.eventTypes;
1398
+ if (options.limit !== void 0) body.limit = options.limit;
1399
+ const response = await this.http.request({
1400
+ method: "POST",
1401
+ path: `/webhooks/${encodeURIComponent(id)}/backfill`,
1402
+ body
1403
+ });
1404
+ return transformKeys(response);
1405
+ }
1319
1406
  /**
1320
1407
  * Rotate the webhook signing secret
1321
1408
  *
@@ -2988,25 +3075,59 @@ var WorkspacesSubResource = class {
2988
3075
  path: `/enterprise/workspaces/${encodeURIComponent(workspaceId)}`
2989
3076
  });
2990
3077
  }
3078
+ /**
3079
+ * Submit (or resubmit) a verification for an enterprise workspace.
3080
+ *
3081
+ * Partial-update friendly (May 2026): for resubmit on an existing
3082
+ * workspace, you only need to send the fields you want to change —
3083
+ * everything else is preserved from the existing record. Hosted page
3084
+ * URLs (`/biz/`, `/opt-in/`, `/legal/`) generated during provision
3085
+ * are auto-preserved.
3086
+ *
3087
+ * For sole proprietors, leave `brn`, `brnType`, `brnCountry` undefined
3088
+ * — the server strips them before forwarding to the carrier.
3089
+ *
3090
+ * @example Full submit
3091
+ * ```ts
3092
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3093
+ * businessName: "Acme LLC",
3094
+ * website: "https://acme.com",
3095
+ * address: { street: "...", city: "...", state: "California", zip: "90001", country: "US" },
3096
+ * contact: { firstName: "...", lastName: "...", email: "...", phone: "+15551234567" },
3097
+ * useCase: "Insurance Services",
3098
+ * useCaseSummary: "...",
3099
+ * sampleMessages: "...",
3100
+ * optInWorkflow: "...",
3101
+ * entityType: "SOLE_PROPRIETOR",
3102
+ * });
3103
+ * ```
3104
+ *
3105
+ * @example Partial-update resubmit (only changing email)
3106
+ * ```ts
3107
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3108
+ * contact: { email: "new@email.com" },
3109
+ * });
3110
+ * ```
3111
+ */
2991
3112
  async submitVerification(workspaceId, data) {
3113
+ const body = {};
3114
+ for (const [k, v] of Object.entries(data)) {
3115
+ if (v !== void 0) body[k] = v;
3116
+ }
2992
3117
  const response = await this.http.request({
2993
3118
  method: "POST",
2994
3119
  path: `/enterprise/workspaces/${encodeURIComponent(workspaceId)}/verification/submit`,
2995
- body: {
2996
- business_name: data.businessName,
2997
- business_type: data.businessType,
2998
- ein: data.ein,
2999
- address: data.address,
3000
- city: data.city,
3001
- state: data.state,
3002
- zip: data.zip,
3003
- use_case: data.useCase,
3004
- sample_messages: data.sampleMessages,
3005
- ...data.monthlyVolume && { monthly_volume: data.monthlyVolume }
3006
- }
3120
+ body
3007
3121
  });
3008
3122
  return transformKeys(response);
3009
3123
  }
3124
+ /**
3125
+ * Convenience alias for resubmits. Reads more naturally when you only
3126
+ * want to update a few fields after a rejection.
3127
+ */
3128
+ async resubmitVerification(workspaceId, partialUpdates) {
3129
+ return this.submitVerification(workspaceId, partialUpdates);
3130
+ }
3010
3131
  async inheritVerification(workspaceId, options) {
3011
3132
  const response = await this.http.request({
3012
3133
  method: "POST",
package/dist/index.mjs CHANGED
@@ -1256,6 +1256,93 @@ var WebhooksResource = class {
1256
1256
  });
1257
1257
  return transformKeys(response);
1258
1258
  }
1259
+ /**
1260
+ * Replay failed or cancelled webhook deliveries from the audit log.
1261
+ *
1262
+ * Use after a customer endpoint has recovered from an outage to re-fire
1263
+ * deliveries that we recorded but couldn't deliver. Each replay creates
1264
+ * a new delivery row preserving the original `event_id` so customers can
1265
+ * dedupe.
1266
+ *
1267
+ * Rejects with HTTP 409 if the circuit is currently open — call
1268
+ * {@link WebhooksResource.resetCircuit} first.
1269
+ *
1270
+ * @param id - Webhook ID
1271
+ * @param options - Window and filter options
1272
+ * @returns Counts of requeued deliveries plus the new delivery IDs
1273
+ *
1274
+ * @example
1275
+ * ```typescript
1276
+ * await sendly.webhooks.resetCircuit('whk_xxx');
1277
+ * const result = await sendly.webhooks.redeliver('whk_xxx', {
1278
+ * since: '2026-05-01T00:00:00Z',
1279
+ * eventTypes: ['message.delivered', 'message.failed'],
1280
+ * limit: 5000,
1281
+ * });
1282
+ * console.log(`Requeued ${result.requeued} deliveries`);
1283
+ * ```
1284
+ */
1285
+ async redeliver(id, options = {}) {
1286
+ if (!id || !id.startsWith("whk_")) {
1287
+ throw new Error("Invalid webhook ID format");
1288
+ }
1289
+ const body = {};
1290
+ if (options.since !== void 0) body.since = options.since;
1291
+ if (options.until !== void 0) body.until = options.until;
1292
+ if (options.eventTypes !== void 0) body.event_types = options.eventTypes;
1293
+ if (options.statuses !== void 0) body.statuses = options.statuses;
1294
+ if (options.limit !== void 0) body.limit = options.limit;
1295
+ const response = await this.http.request({
1296
+ method: "POST",
1297
+ path: `/webhooks/${encodeURIComponent(id)}/redeliver`,
1298
+ body
1299
+ });
1300
+ return transformKeys(response);
1301
+ }
1302
+ /**
1303
+ * Backfill missed webhook events from the underlying message log.
1304
+ *
1305
+ * Use this when a circuit-breaker outage left events with no audit row
1306
+ * (the case `redeliver` cannot recover). The endpoint scans the
1307
+ * `messages` table for the window and synthesizes a webhook delivery
1308
+ * for any message whose `message.sent` / `message.delivered` /
1309
+ * `message.failed` event has not been successfully delivered yet.
1310
+ *
1311
+ * Synthesized events have fresh IDs — your endpoint should dedupe by
1312
+ * `event.data.object.id` (the message ID).
1313
+ *
1314
+ * Rejects with HTTP 409 if the circuit is currently open — call
1315
+ * {@link WebhooksResource.resetCircuit} first.
1316
+ *
1317
+ * @param id - Webhook ID
1318
+ * @param options - Window and filter options
1319
+ * @returns Counts grouped by event type plus the new delivery IDs
1320
+ *
1321
+ * @example
1322
+ * ```typescript
1323
+ * const result = await sendly.webhooks.backfill('whk_xxx', {
1324
+ * since: '2026-05-01T00:00:00Z',
1325
+ * eventTypes: ['message.delivered', 'message.failed'],
1326
+ * });
1327
+ * console.log(`Synthesized ${result.synthesized} events`, result.byType);
1328
+ * ```
1329
+ */
1330
+ async backfill(id, options = {}) {
1331
+ if (!id || !id.startsWith("whk_")) {
1332
+ throw new Error("Invalid webhook ID format");
1333
+ }
1334
+ const body = {};
1335
+ if (options.since !== void 0) body.since = options.since;
1336
+ if (options.until !== void 0) body.until = options.until;
1337
+ if (options.eventTypes !== void 0) body.event_types = options.eventTypes;
1338
+ if (options.limit !== void 0) body.limit = options.limit;
1339
+ const response = await this.http.request({
1340
+ method: "POST",
1341
+ path: `/webhooks/${encodeURIComponent(id)}/backfill`,
1342
+ body
1343
+ });
1344
+ return transformKeys(response);
1345
+ }
1259
1346
  /**
1260
1347
  * Rotate the webhook signing secret
1261
1348
  *
@@ -2928,25 +3015,59 @@ var WorkspacesSubResource = class {
2928
3015
  path: `/enterprise/workspaces/${encodeURIComponent(workspaceId)}`
2929
3016
  });
2930
3017
  }
3018
+ /**
3019
+ * Submit (or resubmit) a verification for an enterprise workspace.
3020
+ *
3021
+ * Partial-update friendly (May 2026): for resubmit on an existing
3022
+ * workspace, you only need to send the fields you want to change —
3023
+ * everything else is preserved from the existing record. Hosted page
3024
+ * URLs (`/biz/`, `/opt-in/`, `/legal/`) generated during provision
3025
+ * are auto-preserved.
3026
+ *
3027
+ * For sole proprietors, leave `brn`, `brnType`, `brnCountry` undefined
3028
+ * — the server strips them before forwarding to the carrier.
3029
+ *
3030
+ * @example Full submit
3031
+ * ```ts
3032
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3033
+ * businessName: "Acme LLC",
3034
+ * website: "https://acme.com",
3035
+ * address: { street: "...", city: "...", state: "California", zip: "90001", country: "US" },
3036
+ * contact: { firstName: "...", lastName: "...", email: "...", phone: "+15551234567" },
3037
+ * useCase: "Insurance Services",
3038
+ * useCaseSummary: "...",
3039
+ * sampleMessages: "...",
3040
+ * optInWorkflow: "...",
3041
+ * entityType: "SOLE_PROPRIETOR",
3042
+ * });
3043
+ * ```
3044
+ *
3045
+ * @example Partial-update resubmit (only changing email)
3046
+ * ```ts
3047
+ * await sendly.enterprise.workspaces.submitVerification(workspaceId, {
3048
+ * contact: { email: "new@email.com" },
3049
+ * });
3050
+ * ```
3051
+ */
2931
3052
  async submitVerification(workspaceId, data) {
3053
+ const body = {};
3054
+ for (const [k, v] of Object.entries(data)) {
3055
+ if (v !== void 0) body[k] = v;
3056
+ }
2932
3057
  const response = await this.http.request({
2933
3058
  method: "POST",
2934
3059
  path: `/enterprise/workspaces/${encodeURIComponent(workspaceId)}/verification/submit`,
2935
- body: {
2936
- business_name: data.businessName,
2937
- business_type: data.businessType,
2938
- ein: data.ein,
2939
- address: data.address,
2940
- city: data.city,
2941
- state: data.state,
2942
- zip: data.zip,
2943
- use_case: data.useCase,
2944
- sample_messages: data.sampleMessages,
2945
- ...data.monthlyVolume && { monthly_volume: data.monthlyVolume }
2946
- }
3060
+ body
2947
3061
  });
2948
3062
  return transformKeys(response);
2949
3063
  }
3064
+ /**
3065
+ * Convenience alias for resubmits. Reads more naturally when you only
3066
+ * want to update a few fields after a rejection.
3067
+ */
3068
+ async resubmitVerification(workspaceId, partialUpdates) {
3069
+ return this.submitVerification(workspaceId, partialUpdates);
3070
+ }
2950
3071
  async inheritVerification(workspaceId, options) {
2951
3072
  const response = await this.http.request({
2952
3073
  method: "POST",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendly/node",
3
- "version": "3.29.0",
3
+ "version": "3.31.0",
4
4
  "description": "Official Sendly Node.js SDK for SMS messaging",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",