@sendly/node 3.15.3 → 3.18.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/dist/index.d.mts CHANGED
@@ -59,12 +59,17 @@ interface SendMessageRequest {
59
59
  * Stored on the message record and included in webhook event payloads.
60
60
  */
61
61
  metadata?: Record<string, any>;
62
+ /**
63
+ * URLs of media files to include as MMS attachments.
64
+ * Must be publicly accessible HTTPS URLs. Max 10 per message.
65
+ */
66
+ mediaUrls?: string[];
62
67
  }
63
68
  /**
64
69
  * Message status values
65
70
  * Note: "sending" was removed as it doesn't exist in the database
66
71
  */
67
- type MessageStatus = "queued" | "sent" | "delivered" | "failed" | "bounced";
72
+ type MessageStatus = "queued" | "sent" | "delivered" | "failed" | "bounced" | "retrying";
68
73
  /**
69
74
  * How the message was sent
70
75
  */
@@ -101,6 +106,14 @@ interface Message {
101
106
  * Error message if status is "failed"
102
107
  */
103
108
  error?: string | null;
109
+ /**
110
+ * Structured error code (e.g., "E001" for invalid number)
111
+ */
112
+ errorCode?: string | null;
113
+ /**
114
+ * Number of retry attempts made
115
+ */
116
+ retryCount?: number;
104
117
  /**
105
118
  * Number of SMS segments (1 per 160 chars)
106
119
  */
@@ -177,6 +190,42 @@ interface MessageListResponse {
177
190
  */
178
191
  count: number;
179
192
  }
193
+ /**
194
+ * An uploaded media file for MMS
195
+ */
196
+ interface MediaFile {
197
+ /**
198
+ * Unique media file identifier
199
+ */
200
+ id: string;
201
+ /**
202
+ * Publicly accessible URL for the media file
203
+ */
204
+ url: string;
205
+ /**
206
+ * MIME type of the file (e.g., "image/jpeg")
207
+ */
208
+ contentType: string;
209
+ /**
210
+ * File size in bytes
211
+ */
212
+ sizeBytes: number;
213
+ }
214
+ /**
215
+ * Options for uploading a media file
216
+ */
217
+ interface MediaUploadOptions {
218
+ /**
219
+ * Filename for the upload
220
+ * @default "upload.jpg"
221
+ */
222
+ filename?: string;
223
+ /**
224
+ * MIME content type
225
+ * @default "image/jpeg"
226
+ */
227
+ contentType?: string;
228
+ }
180
229
  /**
181
230
  * Request payload for scheduling an SMS message
182
231
  */
@@ -625,7 +674,7 @@ declare const ALL_SUPPORTED_COUNTRIES: string[];
625
674
  /**
626
675
  * Webhook event types
627
676
  */
628
- type WebhookEventType = "message.sent" | "message.delivered" | "message.failed" | "message.bounced" | "message.queued";
677
+ type WebhookEventType = "message.sent" | "message.delivered" | "message.failed" | "message.bounced" | "message.retrying" | "message.queued";
629
678
  /**
630
679
  * Webhook mode - filters which events are delivered
631
680
  * - "all": Receives all events (sandbox + production)
@@ -1492,6 +1541,10 @@ declare class HttpClient {
1492
1541
  * Make an HTTP request to the API
1493
1542
  */
1494
1543
  request<T>(options: RequestOptions): Promise<T>;
1544
+ /**
1545
+ * Make an HTTP request with a raw body (for multipart uploads)
1546
+ */
1547
+ requestFormData<T>(path: string, body: FormData, headers?: Record<string, string>): Promise<T>;
1495
1548
  /**
1496
1549
  * Execute the HTTP request
1497
1550
  */
@@ -1928,6 +1981,25 @@ declare class WebhooksResource {
1928
1981
  * ```
1929
1982
  */
1930
1983
  test(id: string): Promise<WebhookTestResult>;
1984
+ /**
1985
+ * Reset the circuit breaker for a webhook
1986
+ *
1987
+ * Manually resets an open circuit breaker so deliveries resume immediately
1988
+ * instead of waiting for the automatic 5-minute recovery.
1989
+ *
1990
+ * @param id - Webhook ID
1991
+ * @returns Reset confirmation with updated webhook
1992
+ *
1993
+ * @example
1994
+ * ```typescript
1995
+ * const result = await sendly.webhooks.resetCircuit('whk_xxx');
1996
+ * console.log(result.message);
1997
+ * ```
1998
+ */
1999
+ resetCircuit(id: string): Promise<{
2000
+ message: string;
2001
+ webhook: Webhook;
2002
+ }>;
1931
2003
  /**
1932
2004
  * Rotate the webhook signing secret
1933
2005
  *
@@ -2927,6 +2999,57 @@ declare class ContactListsResource {
2927
2999
  private transformList;
2928
3000
  }
2929
3001
 
3002
+ /**
3003
+ * Media Resource
3004
+ * @packageDocumentation
3005
+ */
3006
+
3007
+ /**
3008
+ * Media API resource
3009
+ *
3010
+ * @example
3011
+ * ```typescript
3012
+ * // Upload an image and send as MMS
3013
+ * const file = fs.readFileSync('photo.jpg');
3014
+ * const media = await sendly.media.upload(file, {
3015
+ * filename: 'photo.jpg',
3016
+ * contentType: 'image/jpeg'
3017
+ * });
3018
+ *
3019
+ * await sendly.messages.send({
3020
+ * to: '+15551234567',
3021
+ * text: 'Check out this photo!',
3022
+ * mediaUrls: [media.url]
3023
+ * });
3024
+ * ```
3025
+ */
3026
+ declare class MediaResource {
3027
+ private readonly http;
3028
+ constructor(http: HttpClient);
3029
+ /**
3030
+ * Upload a media file for use in MMS messages
3031
+ *
3032
+ * @param file - File data as a Buffer or ReadableStream
3033
+ * @param options - Upload options (filename, content type)
3034
+ * @returns The uploaded media file with URL
3035
+ *
3036
+ * @example
3037
+ * ```typescript
3038
+ * const media = await sendly.media.upload(
3039
+ * fs.readFileSync('image.png'),
3040
+ * { filename: 'image.png', contentType: 'image/png' }
3041
+ * );
3042
+ *
3043
+ * console.log(media.url); // https://...
3044
+ * ```
3045
+ *
3046
+ * @throws {ValidationError} If the file is invalid or too large
3047
+ * @throws {AuthenticationError} If the API key is invalid
3048
+ * @throws {RateLimitError} If rate limit is exceeded
3049
+ */
3050
+ upload(file: Buffer | NodeJS.ReadableStream, options?: MediaUploadOptions): Promise<MediaFile>;
3051
+ }
3052
+
2930
3053
  /**
2931
3054
  * Sendly Client
2932
3055
  * @packageDocumentation
@@ -3097,6 +3220,26 @@ declare class Sendly {
3097
3220
  * ```
3098
3221
  */
3099
3222
  readonly contacts: ContactsResource;
3223
+ /**
3224
+ * Media API resource - Upload media for MMS
3225
+ *
3226
+ * @example
3227
+ * ```typescript
3228
+ * // Upload a file
3229
+ * const media = await sendly.media.upload(fileBuffer, {
3230
+ * filename: 'photo.jpg',
3231
+ * contentType: 'image/jpeg'
3232
+ * });
3233
+ *
3234
+ * // Send as MMS
3235
+ * await sendly.messages.send({
3236
+ * to: '+15551234567',
3237
+ * text: 'Check this out!',
3238
+ * mediaUrls: [media.url]
3239
+ * });
3240
+ * ```
3241
+ */
3242
+ readonly media: MediaResource;
3100
3243
  private readonly http;
3101
3244
  private readonly config;
3102
3245
  /**
@@ -3547,4 +3690,4 @@ declare class Webhooks {
3547
3690
  */
3548
3691
  type WebhookMessageData = WebhookMessageObject;
3549
3692
 
3550
- export { ALL_SUPPORTED_COUNTRIES, type Account, type ApiErrorResponse, type ApiKey, AuthenticationError, type BatchListResponse, type BatchMessageItem, type BatchMessageRequest, type BatchMessageResponse, type BatchMessageResult, type BatchStatus, CREDITS_PER_SMS, type Campaign, type CampaignListResponse, type CampaignPreview, type CampaignStatus, type CancelledMessageResponse, type CheckVerificationRequest, type CheckVerificationResponse, type CircuitState, type Contact, type ContactList, type ContactListResponse, type ContactListsResponse, type CreateCampaignRequest, type CreateContactListRequest, type CreateContactRequest, type CreateTemplateRequest, type CreateVerifySessionRequest, type CreateWebhookOptions, type CreditTransaction, type Credits, type DeliveryStatus, type ImportContactItem, type ImportContactsError, type ImportContactsRequest, type ImportContactsResponse, InsufficientCreditsError, type ListBatchesOptions, type ListCampaignsOptions, type ListContactsOptions, type ListMessagesOptions, type ListScheduledMessagesOptions, type ListVerificationsOptions, type Message, type MessageListResponse, type MessageStatus, type MessageType, NetworkError, NotFoundError, type PricingTier, RateLimitError, type RateLimitInfo, SANDBOX_TEST_NUMBERS, SUPPORTED_COUNTRIES, type ScheduleCampaignRequest, type ScheduleMessageRequest, type ScheduledMessage, type ScheduledMessageListResponse, type ScheduledMessageStatus, type SendMessageRequest, type SendVerificationRequest, type SendVerificationResponse, type SenderType, Sendly, type SendlyConfig, SendlyError, type SendlyErrorCode, type Template, type TemplateListResponse, type TemplatePreview, type TemplateStatus, type TemplateVariable, TimeoutError, type UpdateCampaignRequest, type UpdateContactListRequest, type UpdateContactRequest, type UpdateTemplateRequest, type UpdateWebhookOptions, type ValidateSessionTokenRequest, type ValidateSessionTokenResponse, ValidationError, type Verification, type VerificationDeliveryStatus, type VerificationListResponse, type VerificationStatus, type VerifySession, type VerifySessionStatus, type Webhook, type WebhookCreatedResponse, type WebhookDelivery, type WebhookEvent, type WebhookEventType, type WebhookMessageData, type WebhookMessageStatus, type WebhookSecretRotation, WebhookSignatureError, type WebhookTestResult, Webhooks, calculateSegments, Sendly as default, generateWebhookSignature, getCountryFromPhone, isCountrySupported, parseWebhookEvent, validateMessageText, validatePhoneNumber, validateSenderId, verifyWebhookSignature };
3693
+ export { ALL_SUPPORTED_COUNTRIES, type Account, type ApiErrorResponse, type ApiKey, AuthenticationError, type BatchListResponse, type BatchMessageItem, type BatchMessageRequest, type BatchMessageResponse, type BatchMessageResult, type BatchStatus, CREDITS_PER_SMS, type Campaign, type CampaignListResponse, type CampaignPreview, type CampaignStatus, type CancelledMessageResponse, type CheckVerificationRequest, type CheckVerificationResponse, type CircuitState, type Contact, type ContactList, type ContactListResponse, type ContactListsResponse, type CreateCampaignRequest, type CreateContactListRequest, type CreateContactRequest, type CreateTemplateRequest, type CreateVerifySessionRequest, type CreateWebhookOptions, type CreditTransaction, type Credits, type DeliveryStatus, type ImportContactItem, type ImportContactsError, type ImportContactsRequest, type ImportContactsResponse, InsufficientCreditsError, type ListBatchesOptions, type ListCampaignsOptions, type ListContactsOptions, type ListMessagesOptions, type ListScheduledMessagesOptions, type ListVerificationsOptions, type MediaFile, type MediaUploadOptions, type Message, type MessageListResponse, type MessageStatus, type MessageType, NetworkError, NotFoundError, type PricingTier, RateLimitError, type RateLimitInfo, SANDBOX_TEST_NUMBERS, SUPPORTED_COUNTRIES, type ScheduleCampaignRequest, type ScheduleMessageRequest, type ScheduledMessage, type ScheduledMessageListResponse, type ScheduledMessageStatus, type SendMessageRequest, type SendVerificationRequest, type SendVerificationResponse, type SenderType, Sendly, type SendlyConfig, SendlyError, type SendlyErrorCode, type Template, type TemplateListResponse, type TemplatePreview, type TemplateStatus, type TemplateVariable, TimeoutError, type UpdateCampaignRequest, type UpdateContactListRequest, type UpdateContactRequest, type UpdateTemplateRequest, type UpdateWebhookOptions, type ValidateSessionTokenRequest, type ValidateSessionTokenResponse, ValidationError, type Verification, type VerificationDeliveryStatus, type VerificationListResponse, type VerificationStatus, type VerifySession, type VerifySessionStatus, type Webhook, type WebhookCreatedResponse, type WebhookDelivery, type WebhookEvent, type WebhookEventType, type WebhookMessageData, type WebhookMessageStatus, type WebhookSecretRotation, WebhookSignatureError, type WebhookTestResult, Webhooks, calculateSegments, Sendly as default, generateWebhookSignature, getCountryFromPhone, isCountrySupported, parseWebhookEvent, validateMessageText, validatePhoneNumber, validateSenderId, verifyWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -59,12 +59,17 @@ interface SendMessageRequest {
59
59
  * Stored on the message record and included in webhook event payloads.
60
60
  */
61
61
  metadata?: Record<string, any>;
62
+ /**
63
+ * URLs of media files to include as MMS attachments.
64
+ * Must be publicly accessible HTTPS URLs. Max 10 per message.
65
+ */
66
+ mediaUrls?: string[];
62
67
  }
63
68
  /**
64
69
  * Message status values
65
70
  * Note: "sending" was removed as it doesn't exist in the database
66
71
  */
67
- type MessageStatus = "queued" | "sent" | "delivered" | "failed" | "bounced";
72
+ type MessageStatus = "queued" | "sent" | "delivered" | "failed" | "bounced" | "retrying";
68
73
  /**
69
74
  * How the message was sent
70
75
  */
@@ -101,6 +106,14 @@ interface Message {
101
106
  * Error message if status is "failed"
102
107
  */
103
108
  error?: string | null;
109
+ /**
110
+ * Structured error code (e.g., "E001" for invalid number)
111
+ */
112
+ errorCode?: string | null;
113
+ /**
114
+ * Number of retry attempts made
115
+ */
116
+ retryCount?: number;
104
117
  /**
105
118
  * Number of SMS segments (1 per 160 chars)
106
119
  */
@@ -177,6 +190,42 @@ interface MessageListResponse {
177
190
  */
178
191
  count: number;
179
192
  }
193
+ /**
194
+ * An uploaded media file for MMS
195
+ */
196
+ interface MediaFile {
197
+ /**
198
+ * Unique media file identifier
199
+ */
200
+ id: string;
201
+ /**
202
+ * Publicly accessible URL for the media file
203
+ */
204
+ url: string;
205
+ /**
206
+ * MIME type of the file (e.g., "image/jpeg")
207
+ */
208
+ contentType: string;
209
+ /**
210
+ * File size in bytes
211
+ */
212
+ sizeBytes: number;
213
+ }
214
+ /**
215
+ * Options for uploading a media file
216
+ */
217
+ interface MediaUploadOptions {
218
+ /**
219
+ * Filename for the upload
220
+ * @default "upload.jpg"
221
+ */
222
+ filename?: string;
223
+ /**
224
+ * MIME content type
225
+ * @default "image/jpeg"
226
+ */
227
+ contentType?: string;
228
+ }
180
229
  /**
181
230
  * Request payload for scheduling an SMS message
182
231
  */
@@ -625,7 +674,7 @@ declare const ALL_SUPPORTED_COUNTRIES: string[];
625
674
  /**
626
675
  * Webhook event types
627
676
  */
628
- type WebhookEventType = "message.sent" | "message.delivered" | "message.failed" | "message.bounced" | "message.queued";
677
+ type WebhookEventType = "message.sent" | "message.delivered" | "message.failed" | "message.bounced" | "message.retrying" | "message.queued";
629
678
  /**
630
679
  * Webhook mode - filters which events are delivered
631
680
  * - "all": Receives all events (sandbox + production)
@@ -1492,6 +1541,10 @@ declare class HttpClient {
1492
1541
  * Make an HTTP request to the API
1493
1542
  */
1494
1543
  request<T>(options: RequestOptions): Promise<T>;
1544
+ /**
1545
+ * Make an HTTP request with a raw body (for multipart uploads)
1546
+ */
1547
+ requestFormData<T>(path: string, body: FormData, headers?: Record<string, string>): Promise<T>;
1495
1548
  /**
1496
1549
  * Execute the HTTP request
1497
1550
  */
@@ -1928,6 +1981,25 @@ declare class WebhooksResource {
1928
1981
  * ```
1929
1982
  */
1930
1983
  test(id: string): Promise<WebhookTestResult>;
1984
+ /**
1985
+ * Reset the circuit breaker for a webhook
1986
+ *
1987
+ * Manually resets an open circuit breaker so deliveries resume immediately
1988
+ * instead of waiting for the automatic 5-minute recovery.
1989
+ *
1990
+ * @param id - Webhook ID
1991
+ * @returns Reset confirmation with updated webhook
1992
+ *
1993
+ * @example
1994
+ * ```typescript
1995
+ * const result = await sendly.webhooks.resetCircuit('whk_xxx');
1996
+ * console.log(result.message);
1997
+ * ```
1998
+ */
1999
+ resetCircuit(id: string): Promise<{
2000
+ message: string;
2001
+ webhook: Webhook;
2002
+ }>;
1931
2003
  /**
1932
2004
  * Rotate the webhook signing secret
1933
2005
  *
@@ -2927,6 +2999,57 @@ declare class ContactListsResource {
2927
2999
  private transformList;
2928
3000
  }
2929
3001
 
3002
+ /**
3003
+ * Media Resource
3004
+ * @packageDocumentation
3005
+ */
3006
+
3007
+ /**
3008
+ * Media API resource
3009
+ *
3010
+ * @example
3011
+ * ```typescript
3012
+ * // Upload an image and send as MMS
3013
+ * const file = fs.readFileSync('photo.jpg');
3014
+ * const media = await sendly.media.upload(file, {
3015
+ * filename: 'photo.jpg',
3016
+ * contentType: 'image/jpeg'
3017
+ * });
3018
+ *
3019
+ * await sendly.messages.send({
3020
+ * to: '+15551234567',
3021
+ * text: 'Check out this photo!',
3022
+ * mediaUrls: [media.url]
3023
+ * });
3024
+ * ```
3025
+ */
3026
+ declare class MediaResource {
3027
+ private readonly http;
3028
+ constructor(http: HttpClient);
3029
+ /**
3030
+ * Upload a media file for use in MMS messages
3031
+ *
3032
+ * @param file - File data as a Buffer or ReadableStream
3033
+ * @param options - Upload options (filename, content type)
3034
+ * @returns The uploaded media file with URL
3035
+ *
3036
+ * @example
3037
+ * ```typescript
3038
+ * const media = await sendly.media.upload(
3039
+ * fs.readFileSync('image.png'),
3040
+ * { filename: 'image.png', contentType: 'image/png' }
3041
+ * );
3042
+ *
3043
+ * console.log(media.url); // https://...
3044
+ * ```
3045
+ *
3046
+ * @throws {ValidationError} If the file is invalid or too large
3047
+ * @throws {AuthenticationError} If the API key is invalid
3048
+ * @throws {RateLimitError} If rate limit is exceeded
3049
+ */
3050
+ upload(file: Buffer | NodeJS.ReadableStream, options?: MediaUploadOptions): Promise<MediaFile>;
3051
+ }
3052
+
2930
3053
  /**
2931
3054
  * Sendly Client
2932
3055
  * @packageDocumentation
@@ -3097,6 +3220,26 @@ declare class Sendly {
3097
3220
  * ```
3098
3221
  */
3099
3222
  readonly contacts: ContactsResource;
3223
+ /**
3224
+ * Media API resource - Upload media for MMS
3225
+ *
3226
+ * @example
3227
+ * ```typescript
3228
+ * // Upload a file
3229
+ * const media = await sendly.media.upload(fileBuffer, {
3230
+ * filename: 'photo.jpg',
3231
+ * contentType: 'image/jpeg'
3232
+ * });
3233
+ *
3234
+ * // Send as MMS
3235
+ * await sendly.messages.send({
3236
+ * to: '+15551234567',
3237
+ * text: 'Check this out!',
3238
+ * mediaUrls: [media.url]
3239
+ * });
3240
+ * ```
3241
+ */
3242
+ readonly media: MediaResource;
3100
3243
  private readonly http;
3101
3244
  private readonly config;
3102
3245
  /**
@@ -3547,4 +3690,4 @@ declare class Webhooks {
3547
3690
  */
3548
3691
  type WebhookMessageData = WebhookMessageObject;
3549
3692
 
3550
- export { ALL_SUPPORTED_COUNTRIES, type Account, type ApiErrorResponse, type ApiKey, AuthenticationError, type BatchListResponse, type BatchMessageItem, type BatchMessageRequest, type BatchMessageResponse, type BatchMessageResult, type BatchStatus, CREDITS_PER_SMS, type Campaign, type CampaignListResponse, type CampaignPreview, type CampaignStatus, type CancelledMessageResponse, type CheckVerificationRequest, type CheckVerificationResponse, type CircuitState, type Contact, type ContactList, type ContactListResponse, type ContactListsResponse, type CreateCampaignRequest, type CreateContactListRequest, type CreateContactRequest, type CreateTemplateRequest, type CreateVerifySessionRequest, type CreateWebhookOptions, type CreditTransaction, type Credits, type DeliveryStatus, type ImportContactItem, type ImportContactsError, type ImportContactsRequest, type ImportContactsResponse, InsufficientCreditsError, type ListBatchesOptions, type ListCampaignsOptions, type ListContactsOptions, type ListMessagesOptions, type ListScheduledMessagesOptions, type ListVerificationsOptions, type Message, type MessageListResponse, type MessageStatus, type MessageType, NetworkError, NotFoundError, type PricingTier, RateLimitError, type RateLimitInfo, SANDBOX_TEST_NUMBERS, SUPPORTED_COUNTRIES, type ScheduleCampaignRequest, type ScheduleMessageRequest, type ScheduledMessage, type ScheduledMessageListResponse, type ScheduledMessageStatus, type SendMessageRequest, type SendVerificationRequest, type SendVerificationResponse, type SenderType, Sendly, type SendlyConfig, SendlyError, type SendlyErrorCode, type Template, type TemplateListResponse, type TemplatePreview, type TemplateStatus, type TemplateVariable, TimeoutError, type UpdateCampaignRequest, type UpdateContactListRequest, type UpdateContactRequest, type UpdateTemplateRequest, type UpdateWebhookOptions, type ValidateSessionTokenRequest, type ValidateSessionTokenResponse, ValidationError, type Verification, type VerificationDeliveryStatus, type VerificationListResponse, type VerificationStatus, type VerifySession, type VerifySessionStatus, type Webhook, type WebhookCreatedResponse, type WebhookDelivery, type WebhookEvent, type WebhookEventType, type WebhookMessageData, type WebhookMessageStatus, type WebhookSecretRotation, WebhookSignatureError, type WebhookTestResult, Webhooks, calculateSegments, Sendly as default, generateWebhookSignature, getCountryFromPhone, isCountrySupported, parseWebhookEvent, validateMessageText, validatePhoneNumber, validateSenderId, verifyWebhookSignature };
3693
+ export { ALL_SUPPORTED_COUNTRIES, type Account, type ApiErrorResponse, type ApiKey, AuthenticationError, type BatchListResponse, type BatchMessageItem, type BatchMessageRequest, type BatchMessageResponse, type BatchMessageResult, type BatchStatus, CREDITS_PER_SMS, type Campaign, type CampaignListResponse, type CampaignPreview, type CampaignStatus, type CancelledMessageResponse, type CheckVerificationRequest, type CheckVerificationResponse, type CircuitState, type Contact, type ContactList, type ContactListResponse, type ContactListsResponse, type CreateCampaignRequest, type CreateContactListRequest, type CreateContactRequest, type CreateTemplateRequest, type CreateVerifySessionRequest, type CreateWebhookOptions, type CreditTransaction, type Credits, type DeliveryStatus, type ImportContactItem, type ImportContactsError, type ImportContactsRequest, type ImportContactsResponse, InsufficientCreditsError, type ListBatchesOptions, type ListCampaignsOptions, type ListContactsOptions, type ListMessagesOptions, type ListScheduledMessagesOptions, type ListVerificationsOptions, type MediaFile, type MediaUploadOptions, type Message, type MessageListResponse, type MessageStatus, type MessageType, NetworkError, NotFoundError, type PricingTier, RateLimitError, type RateLimitInfo, SANDBOX_TEST_NUMBERS, SUPPORTED_COUNTRIES, type ScheduleCampaignRequest, type ScheduleMessageRequest, type ScheduledMessage, type ScheduledMessageListResponse, type ScheduledMessageStatus, type SendMessageRequest, type SendVerificationRequest, type SendVerificationResponse, type SenderType, Sendly, type SendlyConfig, SendlyError, type SendlyErrorCode, type Template, type TemplateListResponse, type TemplatePreview, type TemplateStatus, type TemplateVariable, TimeoutError, type UpdateCampaignRequest, type UpdateContactListRequest, type UpdateContactRequest, type UpdateTemplateRequest, type UpdateWebhookOptions, type ValidateSessionTokenRequest, type ValidateSessionTokenResponse, ValidationError, type Verification, type VerificationDeliveryStatus, type VerificationListResponse, type VerificationStatus, type VerifySession, type VerifySessionStatus, type Webhook, type WebhookCreatedResponse, type WebhookDelivery, type WebhookEvent, type WebhookEventType, type WebhookMessageData, type WebhookMessageStatus, type WebhookSecretRotation, WebhookSignatureError, type WebhookTestResult, Webhooks, calculateSegments, Sendly as default, generateWebhookSignature, getCountryFromPhone, isCountrySupported, parseWebhookEvent, validateMessageText, validatePhoneNumber, validateSenderId, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -268,6 +268,44 @@ var HttpClient = class {
268
268
  }
269
269
  throw lastError || new NetworkError("Request failed after retries");
270
270
  }
271
+ /**
272
+ * Make an HTTP request with a raw body (for multipart uploads)
273
+ */
274
+ async requestFormData(path, body, headers = {}) {
275
+ const url = this.buildUrl(path);
276
+ const baseHeaders = this.buildHeaders();
277
+ delete baseHeaders["Content-Type"];
278
+ const mergedHeaders = { ...baseHeaders, ...headers };
279
+ let lastError;
280
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
281
+ try {
282
+ const response = await this.executeRequest(url, {
283
+ method: "POST",
284
+ headers: mergedHeaders,
285
+ body
286
+ });
287
+ this.updateRateLimitInfo(response.headers);
288
+ const data = await this.parseResponse(response);
289
+ return data;
290
+ } catch (error) {
291
+ lastError = error;
292
+ if (error instanceof SendlyError) {
293
+ if (error.statusCode === 401 || error.statusCode === 403 || error.statusCode === 400 || error.statusCode === 404 || error.statusCode === 402) {
294
+ throw error;
295
+ }
296
+ if (error instanceof RateLimitError) {
297
+ throw error;
298
+ }
299
+ }
300
+ if (attempt < this.config.maxRetries) {
301
+ const backoffTime = this.calculateBackoff(attempt);
302
+ await this.sleep(backoffTime);
303
+ continue;
304
+ }
305
+ }
306
+ }
307
+ throw lastError || new NetworkError("Request failed after retries");
308
+ }
271
309
  /**
272
310
  * Execute the HTTP request
273
311
  */
@@ -640,7 +678,8 @@ var MessagesResource = class {
640
678
  text: request.text,
641
679
  ...request.from && { from: request.from },
642
680
  ...request.messageType && { messageType: request.messageType },
643
- ...request.metadata && { metadata: request.metadata }
681
+ ...request.metadata && { metadata: request.metadata },
682
+ ...request.mediaUrls && { mediaUrls: request.mediaUrls }
644
683
  }
645
684
  });
646
685
  return message;
@@ -1246,6 +1285,31 @@ var WebhooksResource = class {
1246
1285
  });
1247
1286
  return transformKeys(response);
1248
1287
  }
1288
+ /**
1289
+ * Reset the circuit breaker for a webhook
1290
+ *
1291
+ * Manually resets an open circuit breaker so deliveries resume immediately
1292
+ * instead of waiting for the automatic 5-minute recovery.
1293
+ *
1294
+ * @param id - Webhook ID
1295
+ * @returns Reset confirmation with updated webhook
1296
+ *
1297
+ * @example
1298
+ * ```typescript
1299
+ * const result = await sendly.webhooks.resetCircuit('whk_xxx');
1300
+ * console.log(result.message);
1301
+ * ```
1302
+ */
1303
+ async resetCircuit(id) {
1304
+ if (!id || !id.startsWith("whk_")) {
1305
+ throw new Error("Invalid webhook ID format");
1306
+ }
1307
+ const response = await this.http.request({
1308
+ method: "POST",
1309
+ path: `/webhooks/${encodeURIComponent(id)}/reset-circuit`
1310
+ });
1311
+ return transformKeys(response);
1312
+ }
1249
1313
  /**
1250
1314
  * Rotate the webhook signing secret
1251
1315
  *
@@ -2732,6 +2796,43 @@ var ContactListsResource = class {
2732
2796
  }
2733
2797
  };
2734
2798
 
2799
+ // src/resources/media.ts
2800
+ var MediaResource = class {
2801
+ http;
2802
+ constructor(http) {
2803
+ this.http = http;
2804
+ }
2805
+ /**
2806
+ * Upload a media file for use in MMS messages
2807
+ *
2808
+ * @param file - File data as a Buffer or ReadableStream
2809
+ * @param options - Upload options (filename, content type)
2810
+ * @returns The uploaded media file with URL
2811
+ *
2812
+ * @example
2813
+ * ```typescript
2814
+ * const media = await sendly.media.upload(
2815
+ * fs.readFileSync('image.png'),
2816
+ * { filename: 'image.png', contentType: 'image/png' }
2817
+ * );
2818
+ *
2819
+ * console.log(media.url); // https://...
2820
+ * ```
2821
+ *
2822
+ * @throws {ValidationError} If the file is invalid or too large
2823
+ * @throws {AuthenticationError} If the API key is invalid
2824
+ * @throws {RateLimitError} If rate limit is exceeded
2825
+ */
2826
+ async upload(file, options) {
2827
+ const filename = options?.filename || "upload.jpg";
2828
+ const contentType = options?.contentType || "image/jpeg";
2829
+ const blob = file instanceof Buffer ? new Blob([file], { type: contentType }) : file;
2830
+ const form = new FormData();
2831
+ form.append("file", blob, filename);
2832
+ return this.http.requestFormData("/media", form);
2833
+ }
2834
+ };
2835
+
2735
2836
  // src/client.ts
2736
2837
  var DEFAULT_BASE_URL2 = "https://sendly.live/api/v1";
2737
2838
  var DEFAULT_TIMEOUT2 = 3e4;
@@ -2870,6 +2971,26 @@ var Sendly = class {
2870
2971
  * ```
2871
2972
  */
2872
2973
  contacts;
2974
+ /**
2975
+ * Media API resource - Upload media for MMS
2976
+ *
2977
+ * @example
2978
+ * ```typescript
2979
+ * // Upload a file
2980
+ * const media = await sendly.media.upload(fileBuffer, {
2981
+ * filename: 'photo.jpg',
2982
+ * contentType: 'image/jpeg'
2983
+ * });
2984
+ *
2985
+ * // Send as MMS
2986
+ * await sendly.messages.send({
2987
+ * to: '+15551234567',
2988
+ * text: 'Check this out!',
2989
+ * mediaUrls: [media.url]
2990
+ * });
2991
+ * ```
2992
+ */
2993
+ media;
2873
2994
  http;
2874
2995
  config;
2875
2996
  /**
@@ -2906,6 +3027,7 @@ var Sendly = class {
2906
3027
  this.templates = new TemplatesResource(this.http);
2907
3028
  this.campaigns = new CampaignsResource(this.http);
2908
3029
  this.contacts = new ContactsResource(this.http);
3030
+ this.media = new MediaResource(this.http);
2909
3031
  }
2910
3032
  /**
2911
3033
  * Check if the client is using a test API key
package/dist/index.mjs CHANGED
@@ -208,6 +208,44 @@ var HttpClient = class {
208
208
  }
209
209
  throw lastError || new NetworkError("Request failed after retries");
210
210
  }
211
+ /**
212
+ * Make an HTTP request with a raw body (for multipart uploads)
213
+ */
214
+ async requestFormData(path, body, headers = {}) {
215
+ const url = this.buildUrl(path);
216
+ const baseHeaders = this.buildHeaders();
217
+ delete baseHeaders["Content-Type"];
218
+ const mergedHeaders = { ...baseHeaders, ...headers };
219
+ let lastError;
220
+ for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {
221
+ try {
222
+ const response = await this.executeRequest(url, {
223
+ method: "POST",
224
+ headers: mergedHeaders,
225
+ body
226
+ });
227
+ this.updateRateLimitInfo(response.headers);
228
+ const data = await this.parseResponse(response);
229
+ return data;
230
+ } catch (error) {
231
+ lastError = error;
232
+ if (error instanceof SendlyError) {
233
+ if (error.statusCode === 401 || error.statusCode === 403 || error.statusCode === 400 || error.statusCode === 404 || error.statusCode === 402) {
234
+ throw error;
235
+ }
236
+ if (error instanceof RateLimitError) {
237
+ throw error;
238
+ }
239
+ }
240
+ if (attempt < this.config.maxRetries) {
241
+ const backoffTime = this.calculateBackoff(attempt);
242
+ await this.sleep(backoffTime);
243
+ continue;
244
+ }
245
+ }
246
+ }
247
+ throw lastError || new NetworkError("Request failed after retries");
248
+ }
211
249
  /**
212
250
  * Execute the HTTP request
213
251
  */
@@ -580,7 +618,8 @@ var MessagesResource = class {
580
618
  text: request.text,
581
619
  ...request.from && { from: request.from },
582
620
  ...request.messageType && { messageType: request.messageType },
583
- ...request.metadata && { metadata: request.metadata }
621
+ ...request.metadata && { metadata: request.metadata },
622
+ ...request.mediaUrls && { mediaUrls: request.mediaUrls }
584
623
  }
585
624
  });
586
625
  return message;
@@ -1186,6 +1225,31 @@ var WebhooksResource = class {
1186
1225
  });
1187
1226
  return transformKeys(response);
1188
1227
  }
1228
+ /**
1229
+ * Reset the circuit breaker for a webhook
1230
+ *
1231
+ * Manually resets an open circuit breaker so deliveries resume immediately
1232
+ * instead of waiting for the automatic 5-minute recovery.
1233
+ *
1234
+ * @param id - Webhook ID
1235
+ * @returns Reset confirmation with updated webhook
1236
+ *
1237
+ * @example
1238
+ * ```typescript
1239
+ * const result = await sendly.webhooks.resetCircuit('whk_xxx');
1240
+ * console.log(result.message);
1241
+ * ```
1242
+ */
1243
+ async resetCircuit(id) {
1244
+ if (!id || !id.startsWith("whk_")) {
1245
+ throw new Error("Invalid webhook ID format");
1246
+ }
1247
+ const response = await this.http.request({
1248
+ method: "POST",
1249
+ path: `/webhooks/${encodeURIComponent(id)}/reset-circuit`
1250
+ });
1251
+ return transformKeys(response);
1252
+ }
1189
1253
  /**
1190
1254
  * Rotate the webhook signing secret
1191
1255
  *
@@ -2672,6 +2736,43 @@ var ContactListsResource = class {
2672
2736
  }
2673
2737
  };
2674
2738
 
2739
+ // src/resources/media.ts
2740
+ var MediaResource = class {
2741
+ http;
2742
+ constructor(http) {
2743
+ this.http = http;
2744
+ }
2745
+ /**
2746
+ * Upload a media file for use in MMS messages
2747
+ *
2748
+ * @param file - File data as a Buffer or ReadableStream
2749
+ * @param options - Upload options (filename, content type)
2750
+ * @returns The uploaded media file with URL
2751
+ *
2752
+ * @example
2753
+ * ```typescript
2754
+ * const media = await sendly.media.upload(
2755
+ * fs.readFileSync('image.png'),
2756
+ * { filename: 'image.png', contentType: 'image/png' }
2757
+ * );
2758
+ *
2759
+ * console.log(media.url); // https://...
2760
+ * ```
2761
+ *
2762
+ * @throws {ValidationError} If the file is invalid or too large
2763
+ * @throws {AuthenticationError} If the API key is invalid
2764
+ * @throws {RateLimitError} If rate limit is exceeded
2765
+ */
2766
+ async upload(file, options) {
2767
+ const filename = options?.filename || "upload.jpg";
2768
+ const contentType = options?.contentType || "image/jpeg";
2769
+ const blob = file instanceof Buffer ? new Blob([file], { type: contentType }) : file;
2770
+ const form = new FormData();
2771
+ form.append("file", blob, filename);
2772
+ return this.http.requestFormData("/media", form);
2773
+ }
2774
+ };
2775
+
2675
2776
  // src/client.ts
2676
2777
  var DEFAULT_BASE_URL2 = "https://sendly.live/api/v1";
2677
2778
  var DEFAULT_TIMEOUT2 = 3e4;
@@ -2810,6 +2911,26 @@ var Sendly = class {
2810
2911
  * ```
2811
2912
  */
2812
2913
  contacts;
2914
+ /**
2915
+ * Media API resource - Upload media for MMS
2916
+ *
2917
+ * @example
2918
+ * ```typescript
2919
+ * // Upload a file
2920
+ * const media = await sendly.media.upload(fileBuffer, {
2921
+ * filename: 'photo.jpg',
2922
+ * contentType: 'image/jpeg'
2923
+ * });
2924
+ *
2925
+ * // Send as MMS
2926
+ * await sendly.messages.send({
2927
+ * to: '+15551234567',
2928
+ * text: 'Check this out!',
2929
+ * mediaUrls: [media.url]
2930
+ * });
2931
+ * ```
2932
+ */
2933
+ media;
2813
2934
  http;
2814
2935
  config;
2815
2936
  /**
@@ -2846,6 +2967,7 @@ var Sendly = class {
2846
2967
  this.templates = new TemplatesResource(this.http);
2847
2968
  this.campaigns = new CampaignsResource(this.http);
2848
2969
  this.contacts = new ContactsResource(this.http);
2970
+ this.media = new MediaResource(this.http);
2849
2971
  }
2850
2972
  /**
2851
2973
  * Check if the client is using a test API key
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendly/node",
3
- "version": "3.15.3",
3
+ "version": "3.18.0",
4
4
  "description": "Official Sendly Node.js SDK for SMS messaging",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",