@semboja/connect 0.5.0 → 0.6.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
@@ -285,11 +285,17 @@ interface SendContactsOptions extends SendMessageOptions {
285
285
  /** Message ID to reply to */
286
286
  replyTo?: string;
287
287
  }
288
+ interface TypingIndicator {
289
+ /** Type of content being composed (currently only "text" is supported) */
290
+ type: 'text';
291
+ }
288
292
  interface MarkAsReadOptions {
289
293
  /** Meta Phone Number ID */
290
294
  phoneNumberId: string;
291
295
  /** Message ID to mark as read */
292
296
  messageId: string;
297
+ /** Show typing indicator after marking as read */
298
+ typingIndicator?: TypingIndicator;
293
299
  }
294
300
  interface MarkAsReadResponseData {
295
301
  success: boolean;
@@ -886,14 +892,25 @@ declare class Messages {
886
892
  /**
887
893
  * Mark a message as read
888
894
  *
895
+ * Optionally show a typing indicator after marking as read.
896
+ * The typing indicator shows "typing..." in the chat for a few seconds.
897
+ *
889
898
  * @param options - Mark as read options
890
899
  * @returns Success response
891
900
  *
892
901
  * @example
893
902
  * ```typescript
903
+ * // Simple mark as read
904
+ * await client.messages.markAsRead({
905
+ * phoneNumberId: '123456789',
906
+ * messageId: 'wamid.xxx',
907
+ * });
908
+ *
909
+ * // Mark as read with typing indicator
894
910
  * await client.messages.markAsRead({
895
911
  * phoneNumberId: '123456789',
896
912
  * messageId: 'wamid.xxx',
913
+ * typingIndicator: { type: 'text' },
897
914
  * });
898
915
  * ```
899
916
  */
@@ -1390,4 +1407,4 @@ declare class NetworkError extends SembojaError {
1390
1407
  * @packageDocumentation
1391
1408
  */
1392
1409
 
1393
- export { type ApiErrorResponse, type ApiResponse, AuthenticationError, type ClientOptions, type Contact, type ContactAddress, type ContactEmail, type ContactName, type ContactOrg, type ContactPhone, type ContactUrl, type CtaUrlActionParameters, type FlowActionParameters, type FlowResponseData, type GetMediaOptions, type InteractiveButton, type InteractiveListSection, type InteractiveMessage, type ListTemplatesOptions, type LocationObject, type MarkAsReadOptions, type MarkAsReadResponseData, type MediaInfo, type MediaObject, type MessageContact, type MessageResponseData, type MessageResult, type MessageType, type MetaBusinessProfile, type MetaPhoneNumberInfo, NetworkError, NotFoundError, type PhoneNumber, type PhoneNumberProfile, RateLimitError, SembojaClient, SembojaError, type SendAudioOptions, type SendContactsOptions, type SendCtaUrlOptions, type SendDocumentOptions, type SendFlowOptions, type SendImageOptions, type SendInteractiveOptions, type SendLocationOptions, type SendMessageOptions, type SendReactionOptions, type SendStickerOptions, type SendTemplateOptions, type SendTextOptions, type SendVideoOptions, ServerError, type StickerObject, type Template, type TemplateComponent, type TemplateInfo, type TemplateLanguage, type TemplateParameter, type TemplateStatus, type TriggerWebhookOptions, type UsageData, type UsageMessages, type UsagePeriod, ValidationError, type VerifyWebhookOptions, type WebhookInteractiveReply, SembojaClient as default, parseFlowResponse, parseWebhookPayload, verifyWebhookSignature };
1410
+ export { type ApiErrorResponse, type ApiResponse, AuthenticationError, type ClientOptions, type Contact, type ContactAddress, type ContactEmail, type ContactName, type ContactOrg, type ContactPhone, type ContactUrl, type CtaUrlActionParameters, type FlowActionParameters, type FlowResponseData, type GetMediaOptions, type InteractiveButton, type InteractiveListSection, type InteractiveMessage, type ListTemplatesOptions, type LocationObject, type MarkAsReadOptions, type MarkAsReadResponseData, type MediaInfo, type MediaObject, type MessageContact, type MessageResponseData, type MessageResult, type MessageType, type MetaBusinessProfile, type MetaPhoneNumberInfo, NetworkError, NotFoundError, type PhoneNumber, type PhoneNumberProfile, RateLimitError, SembojaClient, SembojaError, type SendAudioOptions, type SendContactsOptions, type SendCtaUrlOptions, type SendDocumentOptions, type SendFlowOptions, type SendImageOptions, type SendInteractiveOptions, type SendLocationOptions, type SendMessageOptions, type SendReactionOptions, type SendStickerOptions, type SendTemplateOptions, type SendTextOptions, type SendVideoOptions, ServerError, type StickerObject, type Template, type TemplateComponent, type TemplateInfo, type TemplateLanguage, type TemplateParameter, type TemplateStatus, type TriggerWebhookOptions, type TypingIndicator, type UsageData, type UsageMessages, type UsagePeriod, ValidationError, type VerifyWebhookOptions, type WebhookInteractiveReply, SembojaClient as default, parseFlowResponse, parseWebhookPayload, verifyWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -285,11 +285,17 @@ interface SendContactsOptions extends SendMessageOptions {
285
285
  /** Message ID to reply to */
286
286
  replyTo?: string;
287
287
  }
288
+ interface TypingIndicator {
289
+ /** Type of content being composed (currently only "text" is supported) */
290
+ type: 'text';
291
+ }
288
292
  interface MarkAsReadOptions {
289
293
  /** Meta Phone Number ID */
290
294
  phoneNumberId: string;
291
295
  /** Message ID to mark as read */
292
296
  messageId: string;
297
+ /** Show typing indicator after marking as read */
298
+ typingIndicator?: TypingIndicator;
293
299
  }
294
300
  interface MarkAsReadResponseData {
295
301
  success: boolean;
@@ -886,14 +892,25 @@ declare class Messages {
886
892
  /**
887
893
  * Mark a message as read
888
894
  *
895
+ * Optionally show a typing indicator after marking as read.
896
+ * The typing indicator shows "typing..." in the chat for a few seconds.
897
+ *
889
898
  * @param options - Mark as read options
890
899
  * @returns Success response
891
900
  *
892
901
  * @example
893
902
  * ```typescript
903
+ * // Simple mark as read
904
+ * await client.messages.markAsRead({
905
+ * phoneNumberId: '123456789',
906
+ * messageId: 'wamid.xxx',
907
+ * });
908
+ *
909
+ * // Mark as read with typing indicator
894
910
  * await client.messages.markAsRead({
895
911
  * phoneNumberId: '123456789',
896
912
  * messageId: 'wamid.xxx',
913
+ * typingIndicator: { type: 'text' },
897
914
  * });
898
915
  * ```
899
916
  */
@@ -1390,4 +1407,4 @@ declare class NetworkError extends SembojaError {
1390
1407
  * @packageDocumentation
1391
1408
  */
1392
1409
 
1393
- export { type ApiErrorResponse, type ApiResponse, AuthenticationError, type ClientOptions, type Contact, type ContactAddress, type ContactEmail, type ContactName, type ContactOrg, type ContactPhone, type ContactUrl, type CtaUrlActionParameters, type FlowActionParameters, type FlowResponseData, type GetMediaOptions, type InteractiveButton, type InteractiveListSection, type InteractiveMessage, type ListTemplatesOptions, type LocationObject, type MarkAsReadOptions, type MarkAsReadResponseData, type MediaInfo, type MediaObject, type MessageContact, type MessageResponseData, type MessageResult, type MessageType, type MetaBusinessProfile, type MetaPhoneNumberInfo, NetworkError, NotFoundError, type PhoneNumber, type PhoneNumberProfile, RateLimitError, SembojaClient, SembojaError, type SendAudioOptions, type SendContactsOptions, type SendCtaUrlOptions, type SendDocumentOptions, type SendFlowOptions, type SendImageOptions, type SendInteractiveOptions, type SendLocationOptions, type SendMessageOptions, type SendReactionOptions, type SendStickerOptions, type SendTemplateOptions, type SendTextOptions, type SendVideoOptions, ServerError, type StickerObject, type Template, type TemplateComponent, type TemplateInfo, type TemplateLanguage, type TemplateParameter, type TemplateStatus, type TriggerWebhookOptions, type UsageData, type UsageMessages, type UsagePeriod, ValidationError, type VerifyWebhookOptions, type WebhookInteractiveReply, SembojaClient as default, parseFlowResponse, parseWebhookPayload, verifyWebhookSignature };
1410
+ export { type ApiErrorResponse, type ApiResponse, AuthenticationError, type ClientOptions, type Contact, type ContactAddress, type ContactEmail, type ContactName, type ContactOrg, type ContactPhone, type ContactUrl, type CtaUrlActionParameters, type FlowActionParameters, type FlowResponseData, type GetMediaOptions, type InteractiveButton, type InteractiveListSection, type InteractiveMessage, type ListTemplatesOptions, type LocationObject, type MarkAsReadOptions, type MarkAsReadResponseData, type MediaInfo, type MediaObject, type MessageContact, type MessageResponseData, type MessageResult, type MessageType, type MetaBusinessProfile, type MetaPhoneNumberInfo, NetworkError, NotFoundError, type PhoneNumber, type PhoneNumberProfile, RateLimitError, SembojaClient, SembojaError, type SendAudioOptions, type SendContactsOptions, type SendCtaUrlOptions, type SendDocumentOptions, type SendFlowOptions, type SendImageOptions, type SendInteractiveOptions, type SendLocationOptions, type SendMessageOptions, type SendReactionOptions, type SendStickerOptions, type SendTemplateOptions, type SendTextOptions, type SendVideoOptions, ServerError, type StickerObject, type Template, type TemplateComponent, type TemplateInfo, type TemplateLanguage, type TemplateParameter, type TemplateStatus, type TriggerWebhookOptions, type TypingIndicator, type UsageData, type UsageMessages, type UsagePeriod, ValidationError, type VerifyWebhookOptions, type WebhookInteractiveReply, SembojaClient as default, parseFlowResponse, parseWebhookPayload, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -658,21 +658,33 @@ var Messages = class {
658
658
  /**
659
659
  * Mark a message as read
660
660
  *
661
+ * Optionally show a typing indicator after marking as read.
662
+ * The typing indicator shows "typing..." in the chat for a few seconds.
663
+ *
661
664
  * @param options - Mark as read options
662
665
  * @returns Success response
663
666
  *
664
667
  * @example
665
668
  * ```typescript
669
+ * // Simple mark as read
670
+ * await client.messages.markAsRead({
671
+ * phoneNumberId: '123456789',
672
+ * messageId: 'wamid.xxx',
673
+ * });
674
+ *
675
+ * // Mark as read with typing indicator
666
676
  * await client.messages.markAsRead({
667
677
  * phoneNumberId: '123456789',
668
678
  * messageId: 'wamid.xxx',
679
+ * typingIndicator: { type: 'text' },
669
680
  * });
670
681
  * ```
671
682
  */
672
683
  async markAsRead(options) {
673
684
  return this.http.post("/api/v1/messages/read", {
674
685
  phone_number_id: options.phoneNumberId,
675
- message_id: options.messageId
686
+ message_id: options.messageId,
687
+ typing_indicator: options.typingIndicator
676
688
  });
677
689
  }
678
690
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/lib/http.ts","../src/resources/messages.ts","../src/resources/templates.ts","../src/resources/phone-numbers.ts","../src/resources/usage.ts","../src/resources/test.ts","../src/resources/media.ts","../src/client.ts","../src/webhooks/verify.ts"],"sourcesContent":["/**\n * @semboja/connect - Official Node.js SDK for Semboja WhatsApp API Bridge\n *\n * @example\n * ```typescript\n * // Using default import\n * import Semboja from '@semboja/connect';\n * const client = new Semboja('sk_live_your_api_key');\n *\n * // Or using named import\n * import { SembojaClient, verifyWebhookSignature } from '@semboja/connect';\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // Send a text message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n *\n * // Verify webhook signature\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'],\n * timestamp: req.headers['x-semboja-timestamp'],\n * secret: process.env.WEBHOOK_SECRET,\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n// Main client\nexport { SembojaClient } from './client';\n\n// Default export for convenience\nimport { SembojaClient as Client } from './client';\nexport default Client;\n\n// Webhook utilities\nexport { verifyWebhookSignature, parseWebhookPayload, parseFlowResponse } from './webhooks/verify';\n\n// Error classes\nexport {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from './errors';\n\n// Types\nexport type {\n // Client\n ClientOptions,\n ApiResponse,\n ApiErrorResponse,\n\n // Messages\n MessageType,\n SendMessageOptions,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n FlowActionParameters,\n CtaUrlActionParameters,\n MarkAsReadOptions,\n Template,\n TemplateLanguage,\n TemplateComponent,\n TemplateParameter,\n MediaObject,\n StickerObject,\n LocationObject,\n Contact,\n ContactName,\n ContactPhone,\n ContactEmail,\n ContactAddress,\n ContactUrl,\n ContactOrg,\n InteractiveMessage,\n InteractiveButton,\n InteractiveListSection,\n MessageResponseData,\n MarkAsReadResponseData,\n MessageContact,\n MessageResult,\n\n // Templates\n TemplateStatus,\n TemplateInfo,\n ListTemplatesOptions,\n\n // Phone Numbers\n PhoneNumber,\n PhoneNumberProfile,\n MetaPhoneNumberInfo,\n MetaBusinessProfile,\n\n // Usage\n UsageData,\n UsagePeriod,\n UsageMessages,\n\n // Test\n TriggerWebhookOptions,\n\n // Webhooks\n VerifyWebhookOptions,\n WebhookInteractiveReply,\n FlowResponseData,\n} from './types';\n\n// Media types\nexport type { MediaInfo, GetMediaOptions } from './resources/media';\n","/**\n * Base error class for all Semboja API errors\n */\nexport class SembojaError extends Error {\n /** Error code from the API */\n readonly code: string;\n /** HTTP status code */\n readonly statusCode: number;\n /** Request ID for debugging */\n readonly requestId?: string;\n\n constructor(\n message: string,\n code: string,\n statusCode: number,\n requestId?: string\n ) {\n super(message);\n this.name = 'SembojaError';\n this.code = code;\n this.statusCode = statusCode;\n this.requestId = requestId;\n Object.setPrototypeOf(this, SembojaError.prototype);\n }\n}\n\n/**\n * Authentication error (invalid or missing API key)\n */\nexport class AuthenticationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INVALID_API_KEY', 401, requestId);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Rate limit exceeded error\n */\nexport class RateLimitError extends SembojaError {\n /** When the rate limit resets (Unix timestamp) */\n readonly resetAt?: number;\n\n constructor(message: string, requestId?: string, resetAt?: number) {\n super(message, 'RATE_LIMITED', 429, requestId);\n this.name = 'RateLimitError';\n this.resetAt = resetAt;\n Object.setPrototypeOf(this, RateLimitError.prototype);\n }\n}\n\n/**\n * Validation error (invalid request parameters)\n */\nexport class ValidationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'VALIDATION_ERROR', 400, requestId);\n this.name = 'ValidationError';\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Resource not found error\n */\nexport class NotFoundError extends SembojaError {\n constructor(message: string, code: string, requestId?: string) {\n super(message, code, 404, requestId);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n\n/**\n * Server error from Semboja API\n */\nexport class ServerError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INTERNAL_ERROR', 500, requestId);\n this.name = 'ServerError';\n Object.setPrototypeOf(this, ServerError.prototype);\n }\n}\n\n/**\n * Network or connection error\n */\nexport class NetworkError extends SembojaError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR', 0);\n this.name = 'NetworkError';\n Object.setPrototypeOf(this, NetworkError.prototype);\n }\n}\n","import {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from '../errors';\nimport type { ApiErrorResponse } from '../types';\n\nexport interface HttpClientOptions {\n baseUrl: string;\n apiKey: string;\n timeout: number;\n retries: number;\n}\n\nexport interface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate exponential backoff delay\n */\nfunction getBackoffDelay(attempt: number, baseDelay = 1000): number {\n return Math.min(baseDelay * Math.pow(2, attempt), 30000);\n}\n\n/**\n * HTTP client for making API requests\n */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n private readonly retries: number;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.apiKey = options.apiKey;\n this.timeout = options.timeout;\n this.retries = options.retries;\n }\n\n /**\n * Make an HTTP request with retry logic\n */\n async request<T>(options: RequestOptions): Promise<T> {\n const url = `${this.baseUrl}${options.path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n ...options.headers,\n };\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = await response.json();\n\n // Handle errors\n if (!response.ok) {\n throw this.parseError(response.status, data as ApiErrorResponse);\n }\n\n return data as T;\n } catch (error) {\n lastError = error as Error;\n\n // Don't retry on client errors (4xx) except rate limits\n if (error instanceof SembojaError) {\n if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {\n throw error;\n }\n }\n\n // Don't retry on abort (timeout)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new NetworkError(`Request timeout after ${this.timeout}ms`);\n }\n\n // Retry on network errors and rate limits\n if (attempt < this.retries) {\n const delay = getBackoffDelay(attempt);\n await sleep(delay);\n continue;\n }\n }\n }\n\n // If we get here, all retries failed\n if (lastError instanceof SembojaError) {\n throw lastError;\n }\n throw new NetworkError(lastError?.message || 'Request failed');\n }\n\n /**\n * Parse error response into appropriate error class\n */\n private parseError(statusCode: number, data: ApiErrorResponse): SembojaError {\n const message = data.error?.message || 'Unknown error';\n const code = data.error?.code || 'UNKNOWN_ERROR';\n const requestId = data.meta?.request_id;\n\n switch (statusCode) {\n case 401:\n return new AuthenticationError(message, requestId);\n case 429:\n return new RateLimitError(message, requestId);\n case 400:\n return new ValidationError(message, requestId);\n case 404:\n return new NotFoundError(message, code, requestId);\n case 500:\n case 502:\n case 503:\n return new ServerError(message, requestId);\n default:\n return new SembojaError(message, code, statusCode, requestId);\n }\n }\n\n /**\n * GET request\n */\n async get<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'GET', path });\n }\n\n /**\n * POST request\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'POST', path, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PUT', path, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'DELETE', path });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type {\n ApiResponse,\n MessageResponseData,\n MarkAsReadResponseData,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n MarkAsReadOptions,\n} from '../types';\n\n/**\n * Messages API resource\n *\n * @example\n * ```typescript\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class Messages {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Send a text message\n *\n * @param options - Text message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello, World!',\n * previewUrl: true,\n * });\n * console.log('Message ID:', result.data.messages[0].id);\n * ```\n */\n async sendText(options: SendTextOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/text', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n text: options.text,\n preview_url: options.previewUrl,\n reply_to: options.replyTo,\n });\n }\n\n /**\n * Send a template message\n *\n * @param options - Template message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendTemplate({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * template: {\n * name: 'hello_world',\n * language: { code: 'en' },\n * },\n * });\n * ```\n */\n async sendTemplate(options: SendTemplateOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/template', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n template: options.template,\n });\n }\n\n /**\n * Send an image message\n *\n * @param options - Image message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendImage({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * image: {\n * link: 'https://example.com/image.jpg',\n * caption: 'Check this out!',\n * },\n * });\n * ```\n */\n async sendImage(options: SendImageOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'image',\n image: options.image,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a video message\n *\n * @param options - Video message options\n * @returns Message response with message ID\n */\n async sendVideo(options: SendVideoOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'video',\n video: options.video,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send an audio message\n *\n * @param options - Audio message options\n * @returns Message response with message ID\n */\n async sendAudio(options: SendAudioOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'audio',\n audio: options.audio,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a document message\n *\n * @param options - Document message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendDocument({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * document: {\n * link: 'https://example.com/invoice.pdf',\n * filename: 'invoice.pdf',\n * caption: 'Your invoice',\n * },\n * });\n * ```\n */\n async sendDocument(options: SendDocumentOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'document',\n document: options.document,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a reaction to a message\n *\n * @param options - Reaction options\n * @returns Message response\n *\n * @example\n * ```typescript\n * // Add reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '👍',\n * },\n * });\n *\n * // Remove reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '',\n * },\n * });\n * ```\n */\n async sendReaction(options: SendReactionOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'reaction',\n reaction: {\n message_id: options.reaction.messageId,\n emoji: options.reaction.emoji,\n },\n });\n }\n\n /**\n * Send an interactive message (buttons, lists, etc.)\n *\n * @param options - Interactive message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Button message\n * await client.messages.sendInteractive({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * interactive: {\n * type: 'button',\n * body: { text: 'Choose an option:' },\n * action: {\n * buttons: [\n * { type: 'reply', reply: { id: 'yes', title: 'Yes' } },\n * { type: 'reply', reply: { id: 'no', title: 'No' } },\n * ],\n * },\n * },\n * });\n * ```\n */\n async sendInteractive(\n options: SendInteractiveOptions\n ): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'interactive',\n interactive: options.interactive,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a sticker message\n *\n * @param options - Sticker message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send sticker by URL\n * const result = await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * link: 'https://example.com/sticker.webp',\n * },\n * });\n *\n * // Send sticker by media ID\n * await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * id: 'media_id_here',\n * },\n * });\n * ```\n */\n async sendSticker(options: SendStickerOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'sticker',\n sticker: options.sticker,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a location message\n *\n * @param options - Location message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendLocation({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * location: {\n * latitude: -6.2088,\n * longitude: 106.8456,\n * name: 'Monas',\n * address: 'Gambir, Central Jakarta, Indonesia',\n * },\n * });\n * ```\n */\n async sendLocation(options: SendLocationOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'location',\n location: options.location,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send contact cards\n *\n * @param options - Contact message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendContacts({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * contacts: [\n * {\n * name: {\n * formatted_name: 'John Doe',\n * first_name: 'John',\n * last_name: 'Doe',\n * },\n * phones: [\n * { phone: '+6281234567890', type: 'CELL' },\n * ],\n * emails: [\n * { email: 'john@example.com', type: 'WORK' },\n * ],\n * },\n * ],\n * });\n * ```\n */\n async sendContacts(options: SendContactsOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'contacts',\n contacts: options.contacts,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a WhatsApp Flow message\n *\n * WhatsApp Flows allow you to create structured, multi-screen forms\n * that run entirely inside WhatsApp. The flow must be registered via\n * the Meta Business Manager before sending.\n *\n * @param options - Flow message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a flow with navigate action (most common)\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Please fill in your company details',\n * flowId: '1234567890',\n * flowCta: 'Start Form',\n * flowToken: 'session_abc123',\n * flowAction: 'navigate',\n * screen: 'COMPANY_NAME',\n * });\n *\n * // Send a draft flow for testing\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Test flow',\n * flowId: '1234567890',\n * flowCta: 'Open',\n * mode: 'draft',\n * });\n * ```\n */\n async sendFlow(options: SendFlowOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'flow',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'flow',\n parameters: {\n flow_message_version: '3',\n flow_id: options.flowId,\n flow_cta: options.flowCta,\n flow_token: options.flowToken,\n flow_action: options.flowAction || 'navigate',\n flow_action_payload: options.screen\n ? {\n screen: options.screen,\n data: options.screenData,\n }\n : undefined,\n mode: options.mode,\n },\n },\n },\n });\n }\n\n /**\n * Send a CTA URL button message\n *\n * CTA URL buttons display a button that opens a URL when clicked.\n * This is ideal for payment links, external forms, or any URL you want\n * to present with a clean, branded button instead of a raw link.\n *\n * @param options - CTA URL message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a payment link with a \"Pay Now\" button\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Complete your payment securely',\n * buttonText: 'Pay Now',\n * url: 'https://checkout.stripe.com/pay/cs_xxx',\n * footer: 'Secure payment powered by Stripe',\n * });\n *\n * // Send a booking link\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * header: 'Schedule Your Appointment',\n * body: 'Click below to book your preferred time slot',\n * buttonText: 'Book Now',\n * url: 'https://calendly.com/your-link',\n * });\n * ```\n */\n async sendCtaUrl(options: SendCtaUrlOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'cta_url',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'cta_url',\n parameters: {\n display_text: options.buttonText,\n url: options.url,\n },\n },\n },\n });\n }\n\n /**\n * Mark a message as read\n *\n * @param options - Mark as read options\n * @returns Success response\n *\n * @example\n * ```typescript\n * await client.messages.markAsRead({\n * phoneNumberId: '123456789',\n * messageId: 'wamid.xxx',\n * });\n * ```\n */\n async markAsRead(options: MarkAsReadOptions): Promise<ApiResponse<MarkAsReadResponseData>> {\n return this.http.post('/api/v1/messages/read', {\n phone_number_id: options.phoneNumberId,\n message_id: options.messageId,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TemplateInfo, ListTemplatesOptions } from '../types';\n\n/**\n * Templates API resource\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\nexport class Templates {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all message templates\n * \n * @param options - Optional filters\n * @returns List of templates\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * console.log('Templates:', templates.data);\n * \n * // Filter by status\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\n async list(options?: ListTemplatesOptions): Promise<ApiResponse<TemplateInfo[]>> {\n let path = '/api/v1/templates';\n \n if (options?.status) {\n path += `?status=${encodeURIComponent(options.status)}`;\n }\n \n return this.http.get(path);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, PhoneNumber, PhoneNumberProfile } from '../types';\n\n/**\n * Phone Numbers API resource\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * const profile = await client.phoneNumbers.getProfile('123456');\n * ```\n */\nexport class PhoneNumbers {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all phone numbers configured for your account\n * \n * @returns List of phone numbers\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * for (const phone of phoneNumbers.data) {\n * console.log(`${phone.verified_name}: ${phone.display_phone_number}`);\n * }\n * ```\n */\n async list(): Promise<ApiResponse<PhoneNumber[]>> {\n return this.http.get('/api/v1/phone-numbers');\n }\n\n /**\n * Get a specific phone number by ID\n * \n * @param id - The phone number ID (from list response)\n * @returns Phone number details\n * \n * @example\n * ```typescript\n * const phone = await client.phoneNumbers.get('phone-number-uuid');\n * console.log(phone.data.display_phone_number);\n * ```\n */\n async get(id: string): Promise<ApiResponse<PhoneNumber>> {\n return this.http.get(`/api/v1/phone-numbers/${id}`);\n }\n\n /**\n * Get phone number profile from Meta Graph API\n * \n * Returns detailed information including quality rating, verification status,\n * messaging limits, and business profile.\n * \n * @param id - The phone number ID\n * @returns Phone number profile with Meta API details\n * \n * @example\n * ```typescript\n * const profile = await client.phoneNumbers.getProfile('phone-number-uuid');\n * console.log('Quality:', profile.data.phone_number.quality_rating);\n * console.log('Name:', profile.data.business_profile.about);\n * ```\n */\n async getProfile(id: string): Promise<ApiResponse<PhoneNumberProfile>> {\n return this.http.get(`/api/v1/phone-numbers/${id}/profile`);\n }\n\n /**\n * Update business profile for a phone number\n * \n * @param id - The phone number ID\n * @param profile - Profile fields to update\n * @returns Updated business profile\n * \n * @example\n * ```typescript\n * const updated = await client.phoneNumbers.updateProfile('phone-number-uuid', {\n * about: 'We help businesses grow',\n * description: 'Customer support',\n * email: 'support@example.com',\n * });\n * ```\n */\n async updateProfile(id: string, profile: {\n about?: string;\n address?: string;\n description?: string;\n email?: string;\n vertical?: string;\n websites?: string[];\n }): Promise<ApiResponse<PhoneNumberProfile['business_profile']>> {\n return this.http.patch(`/api/v1/phone-numbers/${id}/profile`, profile);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, UsageData } from '../types';\n\n/**\n * Usage API resource\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Sent: ${usage.data.messages.sent}`);\n * ```\n */\nexport class Usage {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get current usage statistics for the billing period\n * \n * @returns Usage statistics\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Period: ${usage.data.period.start} - ${usage.data.period.end}`);\n * console.log(`Messages sent: ${usage.data.messages.sent}`);\n * console.log(`Messages received: ${usage.data.messages.received}`);\n * console.log(`API calls: ${usage.data.api_calls}`);\n * ```\n */\n async get(): Promise<ApiResponse<UsageData>> {\n return this.http.get('/api/v1/usage');\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TriggerWebhookOptions } from '../types';\n\n/**\n * Test API resource (only works with sk_test_* keys)\n * \n * @example\n * ```typescript\n * // Trigger a test webhook\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Test message',\n * });\n * ```\n */\nexport class Test {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Trigger a test webhook to simulate an incoming message\n * \n * **Note:** This only works with test API keys (sk_test_*)\n * \n * @param options - Webhook trigger options\n * @returns Success response\n * \n * @example\n * ```typescript\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Hello, this is a test incoming message!',\n * });\n * ```\n */\n async triggerWebhook(options: TriggerWebhookOptions): Promise<ApiResponse<{ queued: boolean }>> {\n return this.http.post('/api/v1/test/webhooks/trigger', {\n phone_number_id: options.phoneNumberId,\n type: options.type || 'text',\n from: options.from,\n text: options.text,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\n\n/**\n * Media info returned from WhatsApp API\n */\nexport interface MediaInfo {\n /** Media ID */\n id: string;\n /** Download URL (valid for 5 minutes) */\n url: string;\n /** MIME type (e.g., image/jpeg, application/pdf) */\n mime_type: string;\n /** SHA256 hash of the file */\n sha256: string;\n /** File size in bytes */\n file_size: number;\n /** Always \"whatsapp\" */\n messaging_product: 'whatsapp';\n}\n\n/**\n * Options for getting media info\n */\nexport interface GetMediaOptions {\n /** WhatsApp Business phone number ID */\n phoneNumberId: string;\n /** Media ID from incoming message */\n mediaId: string;\n}\n\n/**\n * Media API\n *\n * Retrieve and download media files sent by users.\n *\n * @example\n * ```typescript\n * // Get media info (URL valid for 5 minutes)\n * const info = await client.media.get({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * console.log(info.url, info.mime_type, info.file_size);\n *\n * // Download the actual file\n * const { data, mimeType } = await client.media.download({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * fs.writeFileSync('file.jpg', data);\n * ```\n */\nexport class Media {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get media info including download URL\n *\n * The returned URL is only valid for 5 minutes.\n * Use `download()` to get the actual file.\n *\n * @param options - Media ID and phone number ID\n * @returns Media info with URL, mime type, and file size\n */\n async get(options: GetMediaOptions): Promise<MediaInfo> {\n const response = await this.http.request<{ success: boolean; data: MediaInfo }>({\n method: 'GET',\n path: `/api/v1/media/${options.mediaId}?phone_number_id=${options.phoneNumberId}`,\n });\n return response.data;\n }\n\n /**\n * Download media file\n *\n * Gets the media info and downloads the actual file binary.\n *\n * @param options - Media ID and phone number ID\n * @returns Buffer containing the file data and its MIME type\n */\n async download(options: GetMediaOptions): Promise<{ data: Buffer; mimeType: string }> {\n // This requires a different HTTP call that returns binary data\n // For now, we need to make a raw fetch request\n const baseUrl = (this.http as any).baseUrl || 'https://connect.semboja.tech';\n const apiKey = (this.http as any).apiKey;\n\n const response = await fetch(\n `${baseUrl}/api/v1/media/${options.mediaId}/download?phone_number_id=${options.phoneNumberId}`,\n {\n method: 'GET',\n headers: {\n 'X-API-Key': apiKey,\n },\n }\n );\n\n if (!response.ok) {\n const errorData = (await response\n .json()\n .catch(() => ({ error: { message: response.statusText } }))) as {\n error?: { message?: string };\n };\n throw new Error(errorData.error?.message || `Download failed: ${response.status}`);\n }\n\n const data = Buffer.from(await response.arrayBuffer());\n const mimeType = response.headers.get('content-type') || 'application/octet-stream';\n\n return { data, mimeType };\n }\n}\n","import { HttpClient } from './lib/http';\nimport { Messages } from './resources/messages';\nimport { Templates } from './resources/templates';\nimport { PhoneNumbers } from './resources/phone-numbers';\nimport { Usage } from './resources/usage';\nimport { Test } from './resources/test';\nimport { Media } from './resources/media';\nimport type { ClientOptions } from './types';\n\nconst DEFAULT_BASE_URL = 'https://connect.semboja.tech';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_RETRIES = 3;\n\n/**\n * Semboja WhatsApp API Client\n *\n * @example\n * ```typescript\n * import { SembojaClient } from '@semboja/connect';\n *\n * // Simple initialization\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // With options\n * const client = new SembojaClient({\n * apiKey: process.env.SEMBOJA_API_KEY!,\n * timeout: 60000,\n * retries: 5,\n * });\n *\n * // Send a message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class SembojaClient {\n /** Messages API */\n readonly messages: Messages;\n\n /** Templates API */\n readonly templates: Templates;\n\n /** Phone Numbers API */\n readonly phoneNumbers: PhoneNumbers;\n\n /** Usage API */\n readonly usage: Usage;\n\n /** Test API (only works with sk_test_* keys) */\n readonly test: Test;\n\n /** Media API (retrieve uploaded files) */\n readonly media: Media;\n\n /** Whether the client is in test mode */\n readonly isTestMode: boolean;\n\n private readonly http: HttpClient;\n\n /**\n * Create a new Semboja client\n *\n * @param optionsOrApiKey - API key string or client options object\n *\n * @example\n * ```typescript\n * // Using API key directly\n * const client = new SembojaClient('sk_live_xxx');\n *\n * // Using options object\n * const client = new SembojaClient({\n * apiKey: 'sk_live_xxx',\n * timeout: 60000,\n * retries: 5,\n * });\n * ```\n */\n constructor(optionsOrApiKey: string | ClientOptions) {\n const options: ClientOptions =\n typeof optionsOrApiKey === 'string' ? { apiKey: optionsOrApiKey } : optionsOrApiKey;\n\n // Validate API key\n if (!options.apiKey) {\n throw new Error('API key is required');\n }\n\n if (!options.apiKey.startsWith('sk_live_') && !options.apiKey.startsWith('sk_test_')) {\n throw new Error('Invalid API key format. Must start with sk_live_ or sk_test_');\n }\n\n // Determine if test mode\n this.isTestMode = options.apiKey.startsWith('sk_test_');\n\n // Create HTTP client\n this.http = new HttpClient({\n baseUrl: options.baseUrl || DEFAULT_BASE_URL,\n apiKey: options.apiKey,\n timeout: options.timeout || DEFAULT_TIMEOUT,\n retries: options.retries ?? DEFAULT_RETRIES,\n });\n\n // Initialize resources\n this.messages = new Messages(this.http);\n this.templates = new Templates(this.http);\n this.phoneNumbers = new PhoneNumbers(this.http);\n this.usage = new Usage(this.http);\n this.test = new Test(this.http);\n this.media = new Media(this.http);\n }\n}\n","import { createHmac, timingSafeEqual } from 'crypto';\nimport type { VerifyWebhookOptions, FlowResponseData } from '../types';\n\n/**\n * Verify a webhook signature from Semboja\n *\n * @param options - Verification options\n * @returns true if the signature is valid, false otherwise\n *\n * @example\n * ```typescript\n * import { verifyWebhookSignature } from '@semboja/connect';\n *\n * // Express.js example\n * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'] as string,\n * timestamp: req.headers['x-semboja-timestamp'] as string,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook\n * const event = JSON.parse(req.body.toString());\n * console.log('Received event:', event);\n *\n * res.status(200).send('OK');\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Hono example\n * app.post('/webhook', async (c) => {\n * const body = await c.req.text();\n * const signature = c.req.header('x-semboja-signature');\n * const timestamp = c.req.header('x-semboja-timestamp');\n *\n * const isValid = verifyWebhookSignature({\n * payload: body,\n * signature: signature!,\n * timestamp: timestamp!,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return c.text('Invalid signature', 401);\n * }\n *\n * const event = JSON.parse(body);\n * // Process the webhook...\n *\n * return c.text('OK');\n * });\n * ```\n */\nexport function verifyWebhookSignature(options: VerifyWebhookOptions): boolean {\n const { payload, signature, timestamp, secret } = options;\n\n // Validate inputs\n if (!payload || !signature || !timestamp || !secret) {\n return false;\n }\n\n // Check timestamp to prevent replay attacks (5 minute tolerance)\n const timestampNum = parseInt(timestamp, 10);\n const now = Math.floor(Date.now() / 1000);\n const tolerance = 5 * 60; // 5 minutes\n\n if (isNaN(timestampNum) || Math.abs(now - timestampNum) > tolerance) {\n return false;\n }\n\n // Stringify payload if it's an object\n const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);\n\n // Compute expected signature (format: timestamp.payload)\n const signatureData = `${timestamp}.${payloadString}`;\n const expectedSignature =\n 'sha256=' + createHmac('sha256', secret).update(signatureData).digest('hex');\n\n // Compare signatures using timing-safe comparison\n try {\n const sigBuffer = Buffer.from(signature);\n const expectedBuffer = Buffer.from(expectedSignature);\n\n if (sigBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(sigBuffer, expectedBuffer);\n } catch {\n return false;\n }\n}\n\n/**\n * Parse a webhook payload into a typed event object\n *\n * @param payload - Raw webhook payload (string or object)\n * @returns Parsed webhook event\n */\nexport function parseWebhookPayload<T = unknown>(payload: string | object): T {\n if (typeof payload === 'string') {\n return JSON.parse(payload) as T;\n }\n return payload as T;\n}\n\n/**\n * Parse the response_json string from a WhatsApp Flow nfm_reply webhook.\n *\n * When a user completes a WhatsApp Flow, the webhook delivers an interactive\n * message with type 'nfm_reply'. The `response_json` field contains a stringified\n * JSON object with the `flow_token` and all data collected by the flow screens.\n *\n * @param responseJson - The response_json string from nfm_reply\n * @returns Parsed flow response data including flow_token and collected fields\n *\n * @example\n * ```typescript\n * import { parseFlowResponse } from '@semboja/connect';\n *\n * // In your webhook handler:\n * if (message.interactive?.type === 'nfm_reply') {\n * const flowData = parseFlowResponse(message.interactive.nfm_reply.response_json);\n * console.log('Flow token:', flowData.flow_token);\n * console.log('Company name:', flowData.company_name);\n * }\n * ```\n */\nexport function parseFlowResponse(responseJson: string): FlowResponseData {\n return JSON.parse(responseJson) as FlowResponseData;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/lib/http.ts","../src/resources/messages.ts","../src/resources/templates.ts","../src/resources/phone-numbers.ts","../src/resources/usage.ts","../src/resources/test.ts","../src/resources/media.ts","../src/client.ts","../src/webhooks/verify.ts"],"sourcesContent":["/**\n * @semboja/connect - Official Node.js SDK for Semboja WhatsApp API Bridge\n *\n * @example\n * ```typescript\n * // Using default import\n * import Semboja from '@semboja/connect';\n * const client = new Semboja('sk_live_your_api_key');\n *\n * // Or using named import\n * import { SembojaClient, verifyWebhookSignature } from '@semboja/connect';\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // Send a text message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n *\n * // Verify webhook signature\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'],\n * timestamp: req.headers['x-semboja-timestamp'],\n * secret: process.env.WEBHOOK_SECRET,\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n// Main client\nexport { SembojaClient } from './client';\n\n// Default export for convenience\nimport { SembojaClient as Client } from './client';\nexport default Client;\n\n// Webhook utilities\nexport { verifyWebhookSignature, parseWebhookPayload, parseFlowResponse } from './webhooks/verify';\n\n// Error classes\nexport {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from './errors';\n\n// Types\nexport type {\n // Client\n ClientOptions,\n ApiResponse,\n ApiErrorResponse,\n\n // Messages\n MessageType,\n SendMessageOptions,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n FlowActionParameters,\n CtaUrlActionParameters,\n MarkAsReadOptions,\n TypingIndicator,\n Template,\n TemplateLanguage,\n TemplateComponent,\n TemplateParameter,\n MediaObject,\n StickerObject,\n LocationObject,\n Contact,\n ContactName,\n ContactPhone,\n ContactEmail,\n ContactAddress,\n ContactUrl,\n ContactOrg,\n InteractiveMessage,\n InteractiveButton,\n InteractiveListSection,\n MessageResponseData,\n MarkAsReadResponseData,\n MessageContact,\n MessageResult,\n\n // Templates\n TemplateStatus,\n TemplateInfo,\n ListTemplatesOptions,\n\n // Phone Numbers\n PhoneNumber,\n PhoneNumberProfile,\n MetaPhoneNumberInfo,\n MetaBusinessProfile,\n\n // Usage\n UsageData,\n UsagePeriod,\n UsageMessages,\n\n // Test\n TriggerWebhookOptions,\n\n // Webhooks\n VerifyWebhookOptions,\n WebhookInteractiveReply,\n FlowResponseData,\n} from './types';\n\n// Media types\nexport type { MediaInfo, GetMediaOptions } from './resources/media';\n","/**\n * Base error class for all Semboja API errors\n */\nexport class SembojaError extends Error {\n /** Error code from the API */\n readonly code: string;\n /** HTTP status code */\n readonly statusCode: number;\n /** Request ID for debugging */\n readonly requestId?: string;\n\n constructor(\n message: string,\n code: string,\n statusCode: number,\n requestId?: string\n ) {\n super(message);\n this.name = 'SembojaError';\n this.code = code;\n this.statusCode = statusCode;\n this.requestId = requestId;\n Object.setPrototypeOf(this, SembojaError.prototype);\n }\n}\n\n/**\n * Authentication error (invalid or missing API key)\n */\nexport class AuthenticationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INVALID_API_KEY', 401, requestId);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Rate limit exceeded error\n */\nexport class RateLimitError extends SembojaError {\n /** When the rate limit resets (Unix timestamp) */\n readonly resetAt?: number;\n\n constructor(message: string, requestId?: string, resetAt?: number) {\n super(message, 'RATE_LIMITED', 429, requestId);\n this.name = 'RateLimitError';\n this.resetAt = resetAt;\n Object.setPrototypeOf(this, RateLimitError.prototype);\n }\n}\n\n/**\n * Validation error (invalid request parameters)\n */\nexport class ValidationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'VALIDATION_ERROR', 400, requestId);\n this.name = 'ValidationError';\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Resource not found error\n */\nexport class NotFoundError extends SembojaError {\n constructor(message: string, code: string, requestId?: string) {\n super(message, code, 404, requestId);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n\n/**\n * Server error from Semboja API\n */\nexport class ServerError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INTERNAL_ERROR', 500, requestId);\n this.name = 'ServerError';\n Object.setPrototypeOf(this, ServerError.prototype);\n }\n}\n\n/**\n * Network or connection error\n */\nexport class NetworkError extends SembojaError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR', 0);\n this.name = 'NetworkError';\n Object.setPrototypeOf(this, NetworkError.prototype);\n }\n}\n","import {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from '../errors';\nimport type { ApiErrorResponse } from '../types';\n\nexport interface HttpClientOptions {\n baseUrl: string;\n apiKey: string;\n timeout: number;\n retries: number;\n}\n\nexport interface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate exponential backoff delay\n */\nfunction getBackoffDelay(attempt: number, baseDelay = 1000): number {\n return Math.min(baseDelay * Math.pow(2, attempt), 30000);\n}\n\n/**\n * HTTP client for making API requests\n */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n private readonly retries: number;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.apiKey = options.apiKey;\n this.timeout = options.timeout;\n this.retries = options.retries;\n }\n\n /**\n * Make an HTTP request with retry logic\n */\n async request<T>(options: RequestOptions): Promise<T> {\n const url = `${this.baseUrl}${options.path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n ...options.headers,\n };\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = await response.json();\n\n // Handle errors\n if (!response.ok) {\n throw this.parseError(response.status, data as ApiErrorResponse);\n }\n\n return data as T;\n } catch (error) {\n lastError = error as Error;\n\n // Don't retry on client errors (4xx) except rate limits\n if (error instanceof SembojaError) {\n if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {\n throw error;\n }\n }\n\n // Don't retry on abort (timeout)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new NetworkError(`Request timeout after ${this.timeout}ms`);\n }\n\n // Retry on network errors and rate limits\n if (attempt < this.retries) {\n const delay = getBackoffDelay(attempt);\n await sleep(delay);\n continue;\n }\n }\n }\n\n // If we get here, all retries failed\n if (lastError instanceof SembojaError) {\n throw lastError;\n }\n throw new NetworkError(lastError?.message || 'Request failed');\n }\n\n /**\n * Parse error response into appropriate error class\n */\n private parseError(statusCode: number, data: ApiErrorResponse): SembojaError {\n const message = data.error?.message || 'Unknown error';\n const code = data.error?.code || 'UNKNOWN_ERROR';\n const requestId = data.meta?.request_id;\n\n switch (statusCode) {\n case 401:\n return new AuthenticationError(message, requestId);\n case 429:\n return new RateLimitError(message, requestId);\n case 400:\n return new ValidationError(message, requestId);\n case 404:\n return new NotFoundError(message, code, requestId);\n case 500:\n case 502:\n case 503:\n return new ServerError(message, requestId);\n default:\n return new SembojaError(message, code, statusCode, requestId);\n }\n }\n\n /**\n * GET request\n */\n async get<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'GET', path });\n }\n\n /**\n * POST request\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'POST', path, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PUT', path, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'DELETE', path });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type {\n ApiResponse,\n MessageResponseData,\n MarkAsReadResponseData,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n MarkAsReadOptions,\n} from '../types';\n\n/**\n * Messages API resource\n *\n * @example\n * ```typescript\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class Messages {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Send a text message\n *\n * @param options - Text message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello, World!',\n * previewUrl: true,\n * });\n * console.log('Message ID:', result.data.messages[0].id);\n * ```\n */\n async sendText(options: SendTextOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/text', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n text: options.text,\n preview_url: options.previewUrl,\n reply_to: options.replyTo,\n });\n }\n\n /**\n * Send a template message\n *\n * @param options - Template message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendTemplate({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * template: {\n * name: 'hello_world',\n * language: { code: 'en' },\n * },\n * });\n * ```\n */\n async sendTemplate(options: SendTemplateOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/template', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n template: options.template,\n });\n }\n\n /**\n * Send an image message\n *\n * @param options - Image message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendImage({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * image: {\n * link: 'https://example.com/image.jpg',\n * caption: 'Check this out!',\n * },\n * });\n * ```\n */\n async sendImage(options: SendImageOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'image',\n image: options.image,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a video message\n *\n * @param options - Video message options\n * @returns Message response with message ID\n */\n async sendVideo(options: SendVideoOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'video',\n video: options.video,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send an audio message\n *\n * @param options - Audio message options\n * @returns Message response with message ID\n */\n async sendAudio(options: SendAudioOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'audio',\n audio: options.audio,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a document message\n *\n * @param options - Document message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendDocument({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * document: {\n * link: 'https://example.com/invoice.pdf',\n * filename: 'invoice.pdf',\n * caption: 'Your invoice',\n * },\n * });\n * ```\n */\n async sendDocument(options: SendDocumentOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'document',\n document: options.document,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a reaction to a message\n *\n * @param options - Reaction options\n * @returns Message response\n *\n * @example\n * ```typescript\n * // Add reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '👍',\n * },\n * });\n *\n * // Remove reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '',\n * },\n * });\n * ```\n */\n async sendReaction(options: SendReactionOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'reaction',\n reaction: {\n message_id: options.reaction.messageId,\n emoji: options.reaction.emoji,\n },\n });\n }\n\n /**\n * Send an interactive message (buttons, lists, etc.)\n *\n * @param options - Interactive message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Button message\n * await client.messages.sendInteractive({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * interactive: {\n * type: 'button',\n * body: { text: 'Choose an option:' },\n * action: {\n * buttons: [\n * { type: 'reply', reply: { id: 'yes', title: 'Yes' } },\n * { type: 'reply', reply: { id: 'no', title: 'No' } },\n * ],\n * },\n * },\n * });\n * ```\n */\n async sendInteractive(\n options: SendInteractiveOptions\n ): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'interactive',\n interactive: options.interactive,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a sticker message\n *\n * @param options - Sticker message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send sticker by URL\n * const result = await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * link: 'https://example.com/sticker.webp',\n * },\n * });\n *\n * // Send sticker by media ID\n * await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * id: 'media_id_here',\n * },\n * });\n * ```\n */\n async sendSticker(options: SendStickerOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'sticker',\n sticker: options.sticker,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a location message\n *\n * @param options - Location message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendLocation({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * location: {\n * latitude: -6.2088,\n * longitude: 106.8456,\n * name: 'Monas',\n * address: 'Gambir, Central Jakarta, Indonesia',\n * },\n * });\n * ```\n */\n async sendLocation(options: SendLocationOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'location',\n location: options.location,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send contact cards\n *\n * @param options - Contact message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendContacts({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * contacts: [\n * {\n * name: {\n * formatted_name: 'John Doe',\n * first_name: 'John',\n * last_name: 'Doe',\n * },\n * phones: [\n * { phone: '+6281234567890', type: 'CELL' },\n * ],\n * emails: [\n * { email: 'john@example.com', type: 'WORK' },\n * ],\n * },\n * ],\n * });\n * ```\n */\n async sendContacts(options: SendContactsOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'contacts',\n contacts: options.contacts,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a WhatsApp Flow message\n *\n * WhatsApp Flows allow you to create structured, multi-screen forms\n * that run entirely inside WhatsApp. The flow must be registered via\n * the Meta Business Manager before sending.\n *\n * @param options - Flow message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a flow with navigate action (most common)\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Please fill in your company details',\n * flowId: '1234567890',\n * flowCta: 'Start Form',\n * flowToken: 'session_abc123',\n * flowAction: 'navigate',\n * screen: 'COMPANY_NAME',\n * });\n *\n * // Send a draft flow for testing\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Test flow',\n * flowId: '1234567890',\n * flowCta: 'Open',\n * mode: 'draft',\n * });\n * ```\n */\n async sendFlow(options: SendFlowOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'flow',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'flow',\n parameters: {\n flow_message_version: '3',\n flow_id: options.flowId,\n flow_cta: options.flowCta,\n flow_token: options.flowToken,\n flow_action: options.flowAction || 'navigate',\n flow_action_payload: options.screen\n ? {\n screen: options.screen,\n data: options.screenData,\n }\n : undefined,\n mode: options.mode,\n },\n },\n },\n });\n }\n\n /**\n * Send a CTA URL button message\n *\n * CTA URL buttons display a button that opens a URL when clicked.\n * This is ideal for payment links, external forms, or any URL you want\n * to present with a clean, branded button instead of a raw link.\n *\n * @param options - CTA URL message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a payment link with a \"Pay Now\" button\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Complete your payment securely',\n * buttonText: 'Pay Now',\n * url: 'https://checkout.stripe.com/pay/cs_xxx',\n * footer: 'Secure payment powered by Stripe',\n * });\n *\n * // Send a booking link\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * header: 'Schedule Your Appointment',\n * body: 'Click below to book your preferred time slot',\n * buttonText: 'Book Now',\n * url: 'https://calendly.com/your-link',\n * });\n * ```\n */\n async sendCtaUrl(options: SendCtaUrlOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'cta_url',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'cta_url',\n parameters: {\n display_text: options.buttonText,\n url: options.url,\n },\n },\n },\n });\n }\n\n /**\n * Mark a message as read\n *\n * Optionally show a typing indicator after marking as read.\n * The typing indicator shows \"typing...\" in the chat for a few seconds.\n *\n * @param options - Mark as read options\n * @returns Success response\n *\n * @example\n * ```typescript\n * // Simple mark as read\n * await client.messages.markAsRead({\n * phoneNumberId: '123456789',\n * messageId: 'wamid.xxx',\n * });\n *\n * // Mark as read with typing indicator\n * await client.messages.markAsRead({\n * phoneNumberId: '123456789',\n * messageId: 'wamid.xxx',\n * typingIndicator: { type: 'text' },\n * });\n * ```\n */\n async markAsRead(options: MarkAsReadOptions): Promise<ApiResponse<MarkAsReadResponseData>> {\n return this.http.post('/api/v1/messages/read', {\n phone_number_id: options.phoneNumberId,\n message_id: options.messageId,\n typing_indicator: options.typingIndicator,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TemplateInfo, ListTemplatesOptions } from '../types';\n\n/**\n * Templates API resource\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\nexport class Templates {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all message templates\n * \n * @param options - Optional filters\n * @returns List of templates\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * console.log('Templates:', templates.data);\n * \n * // Filter by status\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\n async list(options?: ListTemplatesOptions): Promise<ApiResponse<TemplateInfo[]>> {\n let path = '/api/v1/templates';\n \n if (options?.status) {\n path += `?status=${encodeURIComponent(options.status)}`;\n }\n \n return this.http.get(path);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, PhoneNumber, PhoneNumberProfile } from '../types';\n\n/**\n * Phone Numbers API resource\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * const profile = await client.phoneNumbers.getProfile('123456');\n * ```\n */\nexport class PhoneNumbers {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all phone numbers configured for your account\n * \n * @returns List of phone numbers\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * for (const phone of phoneNumbers.data) {\n * console.log(`${phone.verified_name}: ${phone.display_phone_number}`);\n * }\n * ```\n */\n async list(): Promise<ApiResponse<PhoneNumber[]>> {\n return this.http.get('/api/v1/phone-numbers');\n }\n\n /**\n * Get a specific phone number by ID\n * \n * @param id - The phone number ID (from list response)\n * @returns Phone number details\n * \n * @example\n * ```typescript\n * const phone = await client.phoneNumbers.get('phone-number-uuid');\n * console.log(phone.data.display_phone_number);\n * ```\n */\n async get(id: string): Promise<ApiResponse<PhoneNumber>> {\n return this.http.get(`/api/v1/phone-numbers/${id}`);\n }\n\n /**\n * Get phone number profile from Meta Graph API\n * \n * Returns detailed information including quality rating, verification status,\n * messaging limits, and business profile.\n * \n * @param id - The phone number ID\n * @returns Phone number profile with Meta API details\n * \n * @example\n * ```typescript\n * const profile = await client.phoneNumbers.getProfile('phone-number-uuid');\n * console.log('Quality:', profile.data.phone_number.quality_rating);\n * console.log('Name:', profile.data.business_profile.about);\n * ```\n */\n async getProfile(id: string): Promise<ApiResponse<PhoneNumberProfile>> {\n return this.http.get(`/api/v1/phone-numbers/${id}/profile`);\n }\n\n /**\n * Update business profile for a phone number\n * \n * @param id - The phone number ID\n * @param profile - Profile fields to update\n * @returns Updated business profile\n * \n * @example\n * ```typescript\n * const updated = await client.phoneNumbers.updateProfile('phone-number-uuid', {\n * about: 'We help businesses grow',\n * description: 'Customer support',\n * email: 'support@example.com',\n * });\n * ```\n */\n async updateProfile(id: string, profile: {\n about?: string;\n address?: string;\n description?: string;\n email?: string;\n vertical?: string;\n websites?: string[];\n }): Promise<ApiResponse<PhoneNumberProfile['business_profile']>> {\n return this.http.patch(`/api/v1/phone-numbers/${id}/profile`, profile);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, UsageData } from '../types';\n\n/**\n * Usage API resource\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Sent: ${usage.data.messages.sent}`);\n * ```\n */\nexport class Usage {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get current usage statistics for the billing period\n * \n * @returns Usage statistics\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Period: ${usage.data.period.start} - ${usage.data.period.end}`);\n * console.log(`Messages sent: ${usage.data.messages.sent}`);\n * console.log(`Messages received: ${usage.data.messages.received}`);\n * console.log(`API calls: ${usage.data.api_calls}`);\n * ```\n */\n async get(): Promise<ApiResponse<UsageData>> {\n return this.http.get('/api/v1/usage');\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TriggerWebhookOptions } from '../types';\n\n/**\n * Test API resource (only works with sk_test_* keys)\n * \n * @example\n * ```typescript\n * // Trigger a test webhook\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Test message',\n * });\n * ```\n */\nexport class Test {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Trigger a test webhook to simulate an incoming message\n * \n * **Note:** This only works with test API keys (sk_test_*)\n * \n * @param options - Webhook trigger options\n * @returns Success response\n * \n * @example\n * ```typescript\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Hello, this is a test incoming message!',\n * });\n * ```\n */\n async triggerWebhook(options: TriggerWebhookOptions): Promise<ApiResponse<{ queued: boolean }>> {\n return this.http.post('/api/v1/test/webhooks/trigger', {\n phone_number_id: options.phoneNumberId,\n type: options.type || 'text',\n from: options.from,\n text: options.text,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\n\n/**\n * Media info returned from WhatsApp API\n */\nexport interface MediaInfo {\n /** Media ID */\n id: string;\n /** Download URL (valid for 5 minutes) */\n url: string;\n /** MIME type (e.g., image/jpeg, application/pdf) */\n mime_type: string;\n /** SHA256 hash of the file */\n sha256: string;\n /** File size in bytes */\n file_size: number;\n /** Always \"whatsapp\" */\n messaging_product: 'whatsapp';\n}\n\n/**\n * Options for getting media info\n */\nexport interface GetMediaOptions {\n /** WhatsApp Business phone number ID */\n phoneNumberId: string;\n /** Media ID from incoming message */\n mediaId: string;\n}\n\n/**\n * Media API\n *\n * Retrieve and download media files sent by users.\n *\n * @example\n * ```typescript\n * // Get media info (URL valid for 5 minutes)\n * const info = await client.media.get({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * console.log(info.url, info.mime_type, info.file_size);\n *\n * // Download the actual file\n * const { data, mimeType } = await client.media.download({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * fs.writeFileSync('file.jpg', data);\n * ```\n */\nexport class Media {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get media info including download URL\n *\n * The returned URL is only valid for 5 minutes.\n * Use `download()` to get the actual file.\n *\n * @param options - Media ID and phone number ID\n * @returns Media info with URL, mime type, and file size\n */\n async get(options: GetMediaOptions): Promise<MediaInfo> {\n const response = await this.http.request<{ success: boolean; data: MediaInfo }>({\n method: 'GET',\n path: `/api/v1/media/${options.mediaId}?phone_number_id=${options.phoneNumberId}`,\n });\n return response.data;\n }\n\n /**\n * Download media file\n *\n * Gets the media info and downloads the actual file binary.\n *\n * @param options - Media ID and phone number ID\n * @returns Buffer containing the file data and its MIME type\n */\n async download(options: GetMediaOptions): Promise<{ data: Buffer; mimeType: string }> {\n // This requires a different HTTP call that returns binary data\n // For now, we need to make a raw fetch request\n const baseUrl = (this.http as any).baseUrl || 'https://connect.semboja.tech';\n const apiKey = (this.http as any).apiKey;\n\n const response = await fetch(\n `${baseUrl}/api/v1/media/${options.mediaId}/download?phone_number_id=${options.phoneNumberId}`,\n {\n method: 'GET',\n headers: {\n 'X-API-Key': apiKey,\n },\n }\n );\n\n if (!response.ok) {\n const errorData = (await response\n .json()\n .catch(() => ({ error: { message: response.statusText } }))) as {\n error?: { message?: string };\n };\n throw new Error(errorData.error?.message || `Download failed: ${response.status}`);\n }\n\n const data = Buffer.from(await response.arrayBuffer());\n const mimeType = response.headers.get('content-type') || 'application/octet-stream';\n\n return { data, mimeType };\n }\n}\n","import { HttpClient } from './lib/http';\nimport { Messages } from './resources/messages';\nimport { Templates } from './resources/templates';\nimport { PhoneNumbers } from './resources/phone-numbers';\nimport { Usage } from './resources/usage';\nimport { Test } from './resources/test';\nimport { Media } from './resources/media';\nimport type { ClientOptions } from './types';\n\nconst DEFAULT_BASE_URL = 'https://connect.semboja.tech';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_RETRIES = 3;\n\n/**\n * Semboja WhatsApp API Client\n *\n * @example\n * ```typescript\n * import { SembojaClient } from '@semboja/connect';\n *\n * // Simple initialization\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // With options\n * const client = new SembojaClient({\n * apiKey: process.env.SEMBOJA_API_KEY!,\n * timeout: 60000,\n * retries: 5,\n * });\n *\n * // Send a message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class SembojaClient {\n /** Messages API */\n readonly messages: Messages;\n\n /** Templates API */\n readonly templates: Templates;\n\n /** Phone Numbers API */\n readonly phoneNumbers: PhoneNumbers;\n\n /** Usage API */\n readonly usage: Usage;\n\n /** Test API (only works with sk_test_* keys) */\n readonly test: Test;\n\n /** Media API (retrieve uploaded files) */\n readonly media: Media;\n\n /** Whether the client is in test mode */\n readonly isTestMode: boolean;\n\n private readonly http: HttpClient;\n\n /**\n * Create a new Semboja client\n *\n * @param optionsOrApiKey - API key string or client options object\n *\n * @example\n * ```typescript\n * // Using API key directly\n * const client = new SembojaClient('sk_live_xxx');\n *\n * // Using options object\n * const client = new SembojaClient({\n * apiKey: 'sk_live_xxx',\n * timeout: 60000,\n * retries: 5,\n * });\n * ```\n */\n constructor(optionsOrApiKey: string | ClientOptions) {\n const options: ClientOptions =\n typeof optionsOrApiKey === 'string' ? { apiKey: optionsOrApiKey } : optionsOrApiKey;\n\n // Validate API key\n if (!options.apiKey) {\n throw new Error('API key is required');\n }\n\n if (!options.apiKey.startsWith('sk_live_') && !options.apiKey.startsWith('sk_test_')) {\n throw new Error('Invalid API key format. Must start with sk_live_ or sk_test_');\n }\n\n // Determine if test mode\n this.isTestMode = options.apiKey.startsWith('sk_test_');\n\n // Create HTTP client\n this.http = new HttpClient({\n baseUrl: options.baseUrl || DEFAULT_BASE_URL,\n apiKey: options.apiKey,\n timeout: options.timeout || DEFAULT_TIMEOUT,\n retries: options.retries ?? DEFAULT_RETRIES,\n });\n\n // Initialize resources\n this.messages = new Messages(this.http);\n this.templates = new Templates(this.http);\n this.phoneNumbers = new PhoneNumbers(this.http);\n this.usage = new Usage(this.http);\n this.test = new Test(this.http);\n this.media = new Media(this.http);\n }\n}\n","import { createHmac, timingSafeEqual } from 'crypto';\nimport type { VerifyWebhookOptions, FlowResponseData } from '../types';\n\n/**\n * Verify a webhook signature from Semboja\n *\n * @param options - Verification options\n * @returns true if the signature is valid, false otherwise\n *\n * @example\n * ```typescript\n * import { verifyWebhookSignature } from '@semboja/connect';\n *\n * // Express.js example\n * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'] as string,\n * timestamp: req.headers['x-semboja-timestamp'] as string,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook\n * const event = JSON.parse(req.body.toString());\n * console.log('Received event:', event);\n *\n * res.status(200).send('OK');\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Hono example\n * app.post('/webhook', async (c) => {\n * const body = await c.req.text();\n * const signature = c.req.header('x-semboja-signature');\n * const timestamp = c.req.header('x-semboja-timestamp');\n *\n * const isValid = verifyWebhookSignature({\n * payload: body,\n * signature: signature!,\n * timestamp: timestamp!,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return c.text('Invalid signature', 401);\n * }\n *\n * const event = JSON.parse(body);\n * // Process the webhook...\n *\n * return c.text('OK');\n * });\n * ```\n */\nexport function verifyWebhookSignature(options: VerifyWebhookOptions): boolean {\n const { payload, signature, timestamp, secret } = options;\n\n // Validate inputs\n if (!payload || !signature || !timestamp || !secret) {\n return false;\n }\n\n // Check timestamp to prevent replay attacks (5 minute tolerance)\n const timestampNum = parseInt(timestamp, 10);\n const now = Math.floor(Date.now() / 1000);\n const tolerance = 5 * 60; // 5 minutes\n\n if (isNaN(timestampNum) || Math.abs(now - timestampNum) > tolerance) {\n return false;\n }\n\n // Stringify payload if it's an object\n const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);\n\n // Compute expected signature (format: timestamp.payload)\n const signatureData = `${timestamp}.${payloadString}`;\n const expectedSignature =\n 'sha256=' + createHmac('sha256', secret).update(signatureData).digest('hex');\n\n // Compare signatures using timing-safe comparison\n try {\n const sigBuffer = Buffer.from(signature);\n const expectedBuffer = Buffer.from(expectedSignature);\n\n if (sigBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(sigBuffer, expectedBuffer);\n } catch {\n return false;\n }\n}\n\n/**\n * Parse a webhook payload into a typed event object\n *\n * @param payload - Raw webhook payload (string or object)\n * @returns Parsed webhook event\n */\nexport function parseWebhookPayload<T = unknown>(payload: string | object): T {\n if (typeof payload === 'string') {\n return JSON.parse(payload) as T;\n }\n return payload as T;\n}\n\n/**\n * Parse the response_json string from a WhatsApp Flow nfm_reply webhook.\n *\n * When a user completes a WhatsApp Flow, the webhook delivers an interactive\n * message with type 'nfm_reply'. The `response_json` field contains a stringified\n * JSON object with the `flow_token` and all data collected by the flow screens.\n *\n * @param responseJson - The response_json string from nfm_reply\n * @returns Parsed flow response data including flow_token and collected fields\n *\n * @example\n * ```typescript\n * import { parseFlowResponse } from '@semboja/connect';\n *\n * // In your webhook handler:\n * if (message.interactive?.type === 'nfm_reply') {\n * const flowData = parseFlowResponse(message.interactive.nfm_reply.response_json);\n * console.log('Flow token:', flowData.flow_token);\n * console.log('Company name:', flowData.company_name);\n * }\n * ```\n */\nexport function parseFlowResponse(responseJson: string): FlowResponseData {\n return JSON.parse(responseJson) as FlowResponseData;\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs CHANGED
@@ -621,21 +621,33 @@ var Messages = class {
621
621
  /**
622
622
  * Mark a message as read
623
623
  *
624
+ * Optionally show a typing indicator after marking as read.
625
+ * The typing indicator shows "typing..." in the chat for a few seconds.
626
+ *
624
627
  * @param options - Mark as read options
625
628
  * @returns Success response
626
629
  *
627
630
  * @example
628
631
  * ```typescript
632
+ * // Simple mark as read
633
+ * await client.messages.markAsRead({
634
+ * phoneNumberId: '123456789',
635
+ * messageId: 'wamid.xxx',
636
+ * });
637
+ *
638
+ * // Mark as read with typing indicator
629
639
  * await client.messages.markAsRead({
630
640
  * phoneNumberId: '123456789',
631
641
  * messageId: 'wamid.xxx',
642
+ * typingIndicator: { type: 'text' },
632
643
  * });
633
644
  * ```
634
645
  */
635
646
  async markAsRead(options) {
636
647
  return this.http.post("/api/v1/messages/read", {
637
648
  phone_number_id: options.phoneNumberId,
638
- message_id: options.messageId
649
+ message_id: options.messageId,
650
+ typing_indicator: options.typingIndicator
639
651
  });
640
652
  }
641
653
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/lib/http.ts","../src/resources/messages.ts","../src/resources/templates.ts","../src/resources/phone-numbers.ts","../src/resources/usage.ts","../src/resources/test.ts","../src/resources/media.ts","../src/client.ts","../src/webhooks/verify.ts","../src/index.ts"],"sourcesContent":["/**\n * Base error class for all Semboja API errors\n */\nexport class SembojaError extends Error {\n /** Error code from the API */\n readonly code: string;\n /** HTTP status code */\n readonly statusCode: number;\n /** Request ID for debugging */\n readonly requestId?: string;\n\n constructor(\n message: string,\n code: string,\n statusCode: number,\n requestId?: string\n ) {\n super(message);\n this.name = 'SembojaError';\n this.code = code;\n this.statusCode = statusCode;\n this.requestId = requestId;\n Object.setPrototypeOf(this, SembojaError.prototype);\n }\n}\n\n/**\n * Authentication error (invalid or missing API key)\n */\nexport class AuthenticationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INVALID_API_KEY', 401, requestId);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Rate limit exceeded error\n */\nexport class RateLimitError extends SembojaError {\n /** When the rate limit resets (Unix timestamp) */\n readonly resetAt?: number;\n\n constructor(message: string, requestId?: string, resetAt?: number) {\n super(message, 'RATE_LIMITED', 429, requestId);\n this.name = 'RateLimitError';\n this.resetAt = resetAt;\n Object.setPrototypeOf(this, RateLimitError.prototype);\n }\n}\n\n/**\n * Validation error (invalid request parameters)\n */\nexport class ValidationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'VALIDATION_ERROR', 400, requestId);\n this.name = 'ValidationError';\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Resource not found error\n */\nexport class NotFoundError extends SembojaError {\n constructor(message: string, code: string, requestId?: string) {\n super(message, code, 404, requestId);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n\n/**\n * Server error from Semboja API\n */\nexport class ServerError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INTERNAL_ERROR', 500, requestId);\n this.name = 'ServerError';\n Object.setPrototypeOf(this, ServerError.prototype);\n }\n}\n\n/**\n * Network or connection error\n */\nexport class NetworkError extends SembojaError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR', 0);\n this.name = 'NetworkError';\n Object.setPrototypeOf(this, NetworkError.prototype);\n }\n}\n","import {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from '../errors';\nimport type { ApiErrorResponse } from '../types';\n\nexport interface HttpClientOptions {\n baseUrl: string;\n apiKey: string;\n timeout: number;\n retries: number;\n}\n\nexport interface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate exponential backoff delay\n */\nfunction getBackoffDelay(attempt: number, baseDelay = 1000): number {\n return Math.min(baseDelay * Math.pow(2, attempt), 30000);\n}\n\n/**\n * HTTP client for making API requests\n */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n private readonly retries: number;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.apiKey = options.apiKey;\n this.timeout = options.timeout;\n this.retries = options.retries;\n }\n\n /**\n * Make an HTTP request with retry logic\n */\n async request<T>(options: RequestOptions): Promise<T> {\n const url = `${this.baseUrl}${options.path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n ...options.headers,\n };\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = await response.json();\n\n // Handle errors\n if (!response.ok) {\n throw this.parseError(response.status, data as ApiErrorResponse);\n }\n\n return data as T;\n } catch (error) {\n lastError = error as Error;\n\n // Don't retry on client errors (4xx) except rate limits\n if (error instanceof SembojaError) {\n if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {\n throw error;\n }\n }\n\n // Don't retry on abort (timeout)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new NetworkError(`Request timeout after ${this.timeout}ms`);\n }\n\n // Retry on network errors and rate limits\n if (attempt < this.retries) {\n const delay = getBackoffDelay(attempt);\n await sleep(delay);\n continue;\n }\n }\n }\n\n // If we get here, all retries failed\n if (lastError instanceof SembojaError) {\n throw lastError;\n }\n throw new NetworkError(lastError?.message || 'Request failed');\n }\n\n /**\n * Parse error response into appropriate error class\n */\n private parseError(statusCode: number, data: ApiErrorResponse): SembojaError {\n const message = data.error?.message || 'Unknown error';\n const code = data.error?.code || 'UNKNOWN_ERROR';\n const requestId = data.meta?.request_id;\n\n switch (statusCode) {\n case 401:\n return new AuthenticationError(message, requestId);\n case 429:\n return new RateLimitError(message, requestId);\n case 400:\n return new ValidationError(message, requestId);\n case 404:\n return new NotFoundError(message, code, requestId);\n case 500:\n case 502:\n case 503:\n return new ServerError(message, requestId);\n default:\n return new SembojaError(message, code, statusCode, requestId);\n }\n }\n\n /**\n * GET request\n */\n async get<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'GET', path });\n }\n\n /**\n * POST request\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'POST', path, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PUT', path, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'DELETE', path });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type {\n ApiResponse,\n MessageResponseData,\n MarkAsReadResponseData,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n MarkAsReadOptions,\n} from '../types';\n\n/**\n * Messages API resource\n *\n * @example\n * ```typescript\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class Messages {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Send a text message\n *\n * @param options - Text message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello, World!',\n * previewUrl: true,\n * });\n * console.log('Message ID:', result.data.messages[0].id);\n * ```\n */\n async sendText(options: SendTextOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/text', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n text: options.text,\n preview_url: options.previewUrl,\n reply_to: options.replyTo,\n });\n }\n\n /**\n * Send a template message\n *\n * @param options - Template message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendTemplate({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * template: {\n * name: 'hello_world',\n * language: { code: 'en' },\n * },\n * });\n * ```\n */\n async sendTemplate(options: SendTemplateOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/template', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n template: options.template,\n });\n }\n\n /**\n * Send an image message\n *\n * @param options - Image message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendImage({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * image: {\n * link: 'https://example.com/image.jpg',\n * caption: 'Check this out!',\n * },\n * });\n * ```\n */\n async sendImage(options: SendImageOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'image',\n image: options.image,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a video message\n *\n * @param options - Video message options\n * @returns Message response with message ID\n */\n async sendVideo(options: SendVideoOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'video',\n video: options.video,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send an audio message\n *\n * @param options - Audio message options\n * @returns Message response with message ID\n */\n async sendAudio(options: SendAudioOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'audio',\n audio: options.audio,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a document message\n *\n * @param options - Document message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendDocument({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * document: {\n * link: 'https://example.com/invoice.pdf',\n * filename: 'invoice.pdf',\n * caption: 'Your invoice',\n * },\n * });\n * ```\n */\n async sendDocument(options: SendDocumentOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'document',\n document: options.document,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a reaction to a message\n *\n * @param options - Reaction options\n * @returns Message response\n *\n * @example\n * ```typescript\n * // Add reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '👍',\n * },\n * });\n *\n * // Remove reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '',\n * },\n * });\n * ```\n */\n async sendReaction(options: SendReactionOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'reaction',\n reaction: {\n message_id: options.reaction.messageId,\n emoji: options.reaction.emoji,\n },\n });\n }\n\n /**\n * Send an interactive message (buttons, lists, etc.)\n *\n * @param options - Interactive message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Button message\n * await client.messages.sendInteractive({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * interactive: {\n * type: 'button',\n * body: { text: 'Choose an option:' },\n * action: {\n * buttons: [\n * { type: 'reply', reply: { id: 'yes', title: 'Yes' } },\n * { type: 'reply', reply: { id: 'no', title: 'No' } },\n * ],\n * },\n * },\n * });\n * ```\n */\n async sendInteractive(\n options: SendInteractiveOptions\n ): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'interactive',\n interactive: options.interactive,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a sticker message\n *\n * @param options - Sticker message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send sticker by URL\n * const result = await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * link: 'https://example.com/sticker.webp',\n * },\n * });\n *\n * // Send sticker by media ID\n * await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * id: 'media_id_here',\n * },\n * });\n * ```\n */\n async sendSticker(options: SendStickerOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'sticker',\n sticker: options.sticker,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a location message\n *\n * @param options - Location message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendLocation({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * location: {\n * latitude: -6.2088,\n * longitude: 106.8456,\n * name: 'Monas',\n * address: 'Gambir, Central Jakarta, Indonesia',\n * },\n * });\n * ```\n */\n async sendLocation(options: SendLocationOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'location',\n location: options.location,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send contact cards\n *\n * @param options - Contact message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendContacts({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * contacts: [\n * {\n * name: {\n * formatted_name: 'John Doe',\n * first_name: 'John',\n * last_name: 'Doe',\n * },\n * phones: [\n * { phone: '+6281234567890', type: 'CELL' },\n * ],\n * emails: [\n * { email: 'john@example.com', type: 'WORK' },\n * ],\n * },\n * ],\n * });\n * ```\n */\n async sendContacts(options: SendContactsOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'contacts',\n contacts: options.contacts,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a WhatsApp Flow message\n *\n * WhatsApp Flows allow you to create structured, multi-screen forms\n * that run entirely inside WhatsApp. The flow must be registered via\n * the Meta Business Manager before sending.\n *\n * @param options - Flow message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a flow with navigate action (most common)\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Please fill in your company details',\n * flowId: '1234567890',\n * flowCta: 'Start Form',\n * flowToken: 'session_abc123',\n * flowAction: 'navigate',\n * screen: 'COMPANY_NAME',\n * });\n *\n * // Send a draft flow for testing\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Test flow',\n * flowId: '1234567890',\n * flowCta: 'Open',\n * mode: 'draft',\n * });\n * ```\n */\n async sendFlow(options: SendFlowOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'flow',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'flow',\n parameters: {\n flow_message_version: '3',\n flow_id: options.flowId,\n flow_cta: options.flowCta,\n flow_token: options.flowToken,\n flow_action: options.flowAction || 'navigate',\n flow_action_payload: options.screen\n ? {\n screen: options.screen,\n data: options.screenData,\n }\n : undefined,\n mode: options.mode,\n },\n },\n },\n });\n }\n\n /**\n * Send a CTA URL button message\n *\n * CTA URL buttons display a button that opens a URL when clicked.\n * This is ideal for payment links, external forms, or any URL you want\n * to present with a clean, branded button instead of a raw link.\n *\n * @param options - CTA URL message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a payment link with a \"Pay Now\" button\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Complete your payment securely',\n * buttonText: 'Pay Now',\n * url: 'https://checkout.stripe.com/pay/cs_xxx',\n * footer: 'Secure payment powered by Stripe',\n * });\n *\n * // Send a booking link\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * header: 'Schedule Your Appointment',\n * body: 'Click below to book your preferred time slot',\n * buttonText: 'Book Now',\n * url: 'https://calendly.com/your-link',\n * });\n * ```\n */\n async sendCtaUrl(options: SendCtaUrlOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'cta_url',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'cta_url',\n parameters: {\n display_text: options.buttonText,\n url: options.url,\n },\n },\n },\n });\n }\n\n /**\n * Mark a message as read\n *\n * @param options - Mark as read options\n * @returns Success response\n *\n * @example\n * ```typescript\n * await client.messages.markAsRead({\n * phoneNumberId: '123456789',\n * messageId: 'wamid.xxx',\n * });\n * ```\n */\n async markAsRead(options: MarkAsReadOptions): Promise<ApiResponse<MarkAsReadResponseData>> {\n return this.http.post('/api/v1/messages/read', {\n phone_number_id: options.phoneNumberId,\n message_id: options.messageId,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TemplateInfo, ListTemplatesOptions } from '../types';\n\n/**\n * Templates API resource\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\nexport class Templates {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all message templates\n * \n * @param options - Optional filters\n * @returns List of templates\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * console.log('Templates:', templates.data);\n * \n * // Filter by status\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\n async list(options?: ListTemplatesOptions): Promise<ApiResponse<TemplateInfo[]>> {\n let path = '/api/v1/templates';\n \n if (options?.status) {\n path += `?status=${encodeURIComponent(options.status)}`;\n }\n \n return this.http.get(path);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, PhoneNumber, PhoneNumberProfile } from '../types';\n\n/**\n * Phone Numbers API resource\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * const profile = await client.phoneNumbers.getProfile('123456');\n * ```\n */\nexport class PhoneNumbers {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all phone numbers configured for your account\n * \n * @returns List of phone numbers\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * for (const phone of phoneNumbers.data) {\n * console.log(`${phone.verified_name}: ${phone.display_phone_number}`);\n * }\n * ```\n */\n async list(): Promise<ApiResponse<PhoneNumber[]>> {\n return this.http.get('/api/v1/phone-numbers');\n }\n\n /**\n * Get a specific phone number by ID\n * \n * @param id - The phone number ID (from list response)\n * @returns Phone number details\n * \n * @example\n * ```typescript\n * const phone = await client.phoneNumbers.get('phone-number-uuid');\n * console.log(phone.data.display_phone_number);\n * ```\n */\n async get(id: string): Promise<ApiResponse<PhoneNumber>> {\n return this.http.get(`/api/v1/phone-numbers/${id}`);\n }\n\n /**\n * Get phone number profile from Meta Graph API\n * \n * Returns detailed information including quality rating, verification status,\n * messaging limits, and business profile.\n * \n * @param id - The phone number ID\n * @returns Phone number profile with Meta API details\n * \n * @example\n * ```typescript\n * const profile = await client.phoneNumbers.getProfile('phone-number-uuid');\n * console.log('Quality:', profile.data.phone_number.quality_rating);\n * console.log('Name:', profile.data.business_profile.about);\n * ```\n */\n async getProfile(id: string): Promise<ApiResponse<PhoneNumberProfile>> {\n return this.http.get(`/api/v1/phone-numbers/${id}/profile`);\n }\n\n /**\n * Update business profile for a phone number\n * \n * @param id - The phone number ID\n * @param profile - Profile fields to update\n * @returns Updated business profile\n * \n * @example\n * ```typescript\n * const updated = await client.phoneNumbers.updateProfile('phone-number-uuid', {\n * about: 'We help businesses grow',\n * description: 'Customer support',\n * email: 'support@example.com',\n * });\n * ```\n */\n async updateProfile(id: string, profile: {\n about?: string;\n address?: string;\n description?: string;\n email?: string;\n vertical?: string;\n websites?: string[];\n }): Promise<ApiResponse<PhoneNumberProfile['business_profile']>> {\n return this.http.patch(`/api/v1/phone-numbers/${id}/profile`, profile);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, UsageData } from '../types';\n\n/**\n * Usage API resource\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Sent: ${usage.data.messages.sent}`);\n * ```\n */\nexport class Usage {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get current usage statistics for the billing period\n * \n * @returns Usage statistics\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Period: ${usage.data.period.start} - ${usage.data.period.end}`);\n * console.log(`Messages sent: ${usage.data.messages.sent}`);\n * console.log(`Messages received: ${usage.data.messages.received}`);\n * console.log(`API calls: ${usage.data.api_calls}`);\n * ```\n */\n async get(): Promise<ApiResponse<UsageData>> {\n return this.http.get('/api/v1/usage');\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TriggerWebhookOptions } from '../types';\n\n/**\n * Test API resource (only works with sk_test_* keys)\n * \n * @example\n * ```typescript\n * // Trigger a test webhook\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Test message',\n * });\n * ```\n */\nexport class Test {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Trigger a test webhook to simulate an incoming message\n * \n * **Note:** This only works with test API keys (sk_test_*)\n * \n * @param options - Webhook trigger options\n * @returns Success response\n * \n * @example\n * ```typescript\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Hello, this is a test incoming message!',\n * });\n * ```\n */\n async triggerWebhook(options: TriggerWebhookOptions): Promise<ApiResponse<{ queued: boolean }>> {\n return this.http.post('/api/v1/test/webhooks/trigger', {\n phone_number_id: options.phoneNumberId,\n type: options.type || 'text',\n from: options.from,\n text: options.text,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\n\n/**\n * Media info returned from WhatsApp API\n */\nexport interface MediaInfo {\n /** Media ID */\n id: string;\n /** Download URL (valid for 5 minutes) */\n url: string;\n /** MIME type (e.g., image/jpeg, application/pdf) */\n mime_type: string;\n /** SHA256 hash of the file */\n sha256: string;\n /** File size in bytes */\n file_size: number;\n /** Always \"whatsapp\" */\n messaging_product: 'whatsapp';\n}\n\n/**\n * Options for getting media info\n */\nexport interface GetMediaOptions {\n /** WhatsApp Business phone number ID */\n phoneNumberId: string;\n /** Media ID from incoming message */\n mediaId: string;\n}\n\n/**\n * Media API\n *\n * Retrieve and download media files sent by users.\n *\n * @example\n * ```typescript\n * // Get media info (URL valid for 5 minutes)\n * const info = await client.media.get({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * console.log(info.url, info.mime_type, info.file_size);\n *\n * // Download the actual file\n * const { data, mimeType } = await client.media.download({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * fs.writeFileSync('file.jpg', data);\n * ```\n */\nexport class Media {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get media info including download URL\n *\n * The returned URL is only valid for 5 minutes.\n * Use `download()` to get the actual file.\n *\n * @param options - Media ID and phone number ID\n * @returns Media info with URL, mime type, and file size\n */\n async get(options: GetMediaOptions): Promise<MediaInfo> {\n const response = await this.http.request<{ success: boolean; data: MediaInfo }>({\n method: 'GET',\n path: `/api/v1/media/${options.mediaId}?phone_number_id=${options.phoneNumberId}`,\n });\n return response.data;\n }\n\n /**\n * Download media file\n *\n * Gets the media info and downloads the actual file binary.\n *\n * @param options - Media ID and phone number ID\n * @returns Buffer containing the file data and its MIME type\n */\n async download(options: GetMediaOptions): Promise<{ data: Buffer; mimeType: string }> {\n // This requires a different HTTP call that returns binary data\n // For now, we need to make a raw fetch request\n const baseUrl = (this.http as any).baseUrl || 'https://connect.semboja.tech';\n const apiKey = (this.http as any).apiKey;\n\n const response = await fetch(\n `${baseUrl}/api/v1/media/${options.mediaId}/download?phone_number_id=${options.phoneNumberId}`,\n {\n method: 'GET',\n headers: {\n 'X-API-Key': apiKey,\n },\n }\n );\n\n if (!response.ok) {\n const errorData = (await response\n .json()\n .catch(() => ({ error: { message: response.statusText } }))) as {\n error?: { message?: string };\n };\n throw new Error(errorData.error?.message || `Download failed: ${response.status}`);\n }\n\n const data = Buffer.from(await response.arrayBuffer());\n const mimeType = response.headers.get('content-type') || 'application/octet-stream';\n\n return { data, mimeType };\n }\n}\n","import { HttpClient } from './lib/http';\nimport { Messages } from './resources/messages';\nimport { Templates } from './resources/templates';\nimport { PhoneNumbers } from './resources/phone-numbers';\nimport { Usage } from './resources/usage';\nimport { Test } from './resources/test';\nimport { Media } from './resources/media';\nimport type { ClientOptions } from './types';\n\nconst DEFAULT_BASE_URL = 'https://connect.semboja.tech';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_RETRIES = 3;\n\n/**\n * Semboja WhatsApp API Client\n *\n * @example\n * ```typescript\n * import { SembojaClient } from '@semboja/connect';\n *\n * // Simple initialization\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // With options\n * const client = new SembojaClient({\n * apiKey: process.env.SEMBOJA_API_KEY!,\n * timeout: 60000,\n * retries: 5,\n * });\n *\n * // Send a message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class SembojaClient {\n /** Messages API */\n readonly messages: Messages;\n\n /** Templates API */\n readonly templates: Templates;\n\n /** Phone Numbers API */\n readonly phoneNumbers: PhoneNumbers;\n\n /** Usage API */\n readonly usage: Usage;\n\n /** Test API (only works with sk_test_* keys) */\n readonly test: Test;\n\n /** Media API (retrieve uploaded files) */\n readonly media: Media;\n\n /** Whether the client is in test mode */\n readonly isTestMode: boolean;\n\n private readonly http: HttpClient;\n\n /**\n * Create a new Semboja client\n *\n * @param optionsOrApiKey - API key string or client options object\n *\n * @example\n * ```typescript\n * // Using API key directly\n * const client = new SembojaClient('sk_live_xxx');\n *\n * // Using options object\n * const client = new SembojaClient({\n * apiKey: 'sk_live_xxx',\n * timeout: 60000,\n * retries: 5,\n * });\n * ```\n */\n constructor(optionsOrApiKey: string | ClientOptions) {\n const options: ClientOptions =\n typeof optionsOrApiKey === 'string' ? { apiKey: optionsOrApiKey } : optionsOrApiKey;\n\n // Validate API key\n if (!options.apiKey) {\n throw new Error('API key is required');\n }\n\n if (!options.apiKey.startsWith('sk_live_') && !options.apiKey.startsWith('sk_test_')) {\n throw new Error('Invalid API key format. Must start with sk_live_ or sk_test_');\n }\n\n // Determine if test mode\n this.isTestMode = options.apiKey.startsWith('sk_test_');\n\n // Create HTTP client\n this.http = new HttpClient({\n baseUrl: options.baseUrl || DEFAULT_BASE_URL,\n apiKey: options.apiKey,\n timeout: options.timeout || DEFAULT_TIMEOUT,\n retries: options.retries ?? DEFAULT_RETRIES,\n });\n\n // Initialize resources\n this.messages = new Messages(this.http);\n this.templates = new Templates(this.http);\n this.phoneNumbers = new PhoneNumbers(this.http);\n this.usage = new Usage(this.http);\n this.test = new Test(this.http);\n this.media = new Media(this.http);\n }\n}\n","import { createHmac, timingSafeEqual } from 'crypto';\nimport type { VerifyWebhookOptions, FlowResponseData } from '../types';\n\n/**\n * Verify a webhook signature from Semboja\n *\n * @param options - Verification options\n * @returns true if the signature is valid, false otherwise\n *\n * @example\n * ```typescript\n * import { verifyWebhookSignature } from '@semboja/connect';\n *\n * // Express.js example\n * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'] as string,\n * timestamp: req.headers['x-semboja-timestamp'] as string,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook\n * const event = JSON.parse(req.body.toString());\n * console.log('Received event:', event);\n *\n * res.status(200).send('OK');\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Hono example\n * app.post('/webhook', async (c) => {\n * const body = await c.req.text();\n * const signature = c.req.header('x-semboja-signature');\n * const timestamp = c.req.header('x-semboja-timestamp');\n *\n * const isValid = verifyWebhookSignature({\n * payload: body,\n * signature: signature!,\n * timestamp: timestamp!,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return c.text('Invalid signature', 401);\n * }\n *\n * const event = JSON.parse(body);\n * // Process the webhook...\n *\n * return c.text('OK');\n * });\n * ```\n */\nexport function verifyWebhookSignature(options: VerifyWebhookOptions): boolean {\n const { payload, signature, timestamp, secret } = options;\n\n // Validate inputs\n if (!payload || !signature || !timestamp || !secret) {\n return false;\n }\n\n // Check timestamp to prevent replay attacks (5 minute tolerance)\n const timestampNum = parseInt(timestamp, 10);\n const now = Math.floor(Date.now() / 1000);\n const tolerance = 5 * 60; // 5 minutes\n\n if (isNaN(timestampNum) || Math.abs(now - timestampNum) > tolerance) {\n return false;\n }\n\n // Stringify payload if it's an object\n const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);\n\n // Compute expected signature (format: timestamp.payload)\n const signatureData = `${timestamp}.${payloadString}`;\n const expectedSignature =\n 'sha256=' + createHmac('sha256', secret).update(signatureData).digest('hex');\n\n // Compare signatures using timing-safe comparison\n try {\n const sigBuffer = Buffer.from(signature);\n const expectedBuffer = Buffer.from(expectedSignature);\n\n if (sigBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(sigBuffer, expectedBuffer);\n } catch {\n return false;\n }\n}\n\n/**\n * Parse a webhook payload into a typed event object\n *\n * @param payload - Raw webhook payload (string or object)\n * @returns Parsed webhook event\n */\nexport function parseWebhookPayload<T = unknown>(payload: string | object): T {\n if (typeof payload === 'string') {\n return JSON.parse(payload) as T;\n }\n return payload as T;\n}\n\n/**\n * Parse the response_json string from a WhatsApp Flow nfm_reply webhook.\n *\n * When a user completes a WhatsApp Flow, the webhook delivers an interactive\n * message with type 'nfm_reply'. The `response_json` field contains a stringified\n * JSON object with the `flow_token` and all data collected by the flow screens.\n *\n * @param responseJson - The response_json string from nfm_reply\n * @returns Parsed flow response data including flow_token and collected fields\n *\n * @example\n * ```typescript\n * import { parseFlowResponse } from '@semboja/connect';\n *\n * // In your webhook handler:\n * if (message.interactive?.type === 'nfm_reply') {\n * const flowData = parseFlowResponse(message.interactive.nfm_reply.response_json);\n * console.log('Flow token:', flowData.flow_token);\n * console.log('Company name:', flowData.company_name);\n * }\n * ```\n */\nexport function parseFlowResponse(responseJson: string): FlowResponseData {\n return JSON.parse(responseJson) as FlowResponseData;\n}\n","/**\n * @semboja/connect - Official Node.js SDK for Semboja WhatsApp API Bridge\n *\n * @example\n * ```typescript\n * // Using default import\n * import Semboja from '@semboja/connect';\n * const client = new Semboja('sk_live_your_api_key');\n *\n * // Or using named import\n * import { SembojaClient, verifyWebhookSignature } from '@semboja/connect';\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // Send a text message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n *\n * // Verify webhook signature\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'],\n * timestamp: req.headers['x-semboja-timestamp'],\n * secret: process.env.WEBHOOK_SECRET,\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n// Main client\nexport { SembojaClient } from './client';\n\n// Default export for convenience\nimport { SembojaClient as Client } from './client';\nexport default Client;\n\n// Webhook utilities\nexport { verifyWebhookSignature, parseWebhookPayload, parseFlowResponse } from './webhooks/verify';\n\n// Error classes\nexport {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from './errors';\n\n// Types\nexport type {\n // Client\n ClientOptions,\n ApiResponse,\n ApiErrorResponse,\n\n // Messages\n MessageType,\n SendMessageOptions,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n FlowActionParameters,\n CtaUrlActionParameters,\n MarkAsReadOptions,\n Template,\n TemplateLanguage,\n TemplateComponent,\n TemplateParameter,\n MediaObject,\n StickerObject,\n LocationObject,\n Contact,\n ContactName,\n ContactPhone,\n ContactEmail,\n ContactAddress,\n ContactUrl,\n ContactOrg,\n InteractiveMessage,\n InteractiveButton,\n InteractiveListSection,\n MessageResponseData,\n MarkAsReadResponseData,\n MessageContact,\n MessageResult,\n\n // Templates\n TemplateStatus,\n TemplateInfo,\n ListTemplatesOptions,\n\n // Phone Numbers\n PhoneNumber,\n PhoneNumberProfile,\n MetaPhoneNumberInfo,\n MetaBusinessProfile,\n\n // Usage\n UsageData,\n UsagePeriod,\n UsageMessages,\n\n // Test\n TriggerWebhookOptions,\n\n // Webhooks\n VerifyWebhookOptions,\n WebhookInteractiveReply,\n FlowResponseData,\n} from './types';\n\n// Media types\nexport type { MediaInfo, GetMediaOptions } from './resources/media';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/lib/http.ts","../src/resources/messages.ts","../src/resources/templates.ts","../src/resources/phone-numbers.ts","../src/resources/usage.ts","../src/resources/test.ts","../src/resources/media.ts","../src/client.ts","../src/webhooks/verify.ts","../src/index.ts"],"sourcesContent":["/**\n * Base error class for all Semboja API errors\n */\nexport class SembojaError extends Error {\n /** Error code from the API */\n readonly code: string;\n /** HTTP status code */\n readonly statusCode: number;\n /** Request ID for debugging */\n readonly requestId?: string;\n\n constructor(\n message: string,\n code: string,\n statusCode: number,\n requestId?: string\n ) {\n super(message);\n this.name = 'SembojaError';\n this.code = code;\n this.statusCode = statusCode;\n this.requestId = requestId;\n Object.setPrototypeOf(this, SembojaError.prototype);\n }\n}\n\n/**\n * Authentication error (invalid or missing API key)\n */\nexport class AuthenticationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INVALID_API_KEY', 401, requestId);\n this.name = 'AuthenticationError';\n Object.setPrototypeOf(this, AuthenticationError.prototype);\n }\n}\n\n/**\n * Rate limit exceeded error\n */\nexport class RateLimitError extends SembojaError {\n /** When the rate limit resets (Unix timestamp) */\n readonly resetAt?: number;\n\n constructor(message: string, requestId?: string, resetAt?: number) {\n super(message, 'RATE_LIMITED', 429, requestId);\n this.name = 'RateLimitError';\n this.resetAt = resetAt;\n Object.setPrototypeOf(this, RateLimitError.prototype);\n }\n}\n\n/**\n * Validation error (invalid request parameters)\n */\nexport class ValidationError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'VALIDATION_ERROR', 400, requestId);\n this.name = 'ValidationError';\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\n/**\n * Resource not found error\n */\nexport class NotFoundError extends SembojaError {\n constructor(message: string, code: string, requestId?: string) {\n super(message, code, 404, requestId);\n this.name = 'NotFoundError';\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n\n/**\n * Server error from Semboja API\n */\nexport class ServerError extends SembojaError {\n constructor(message: string, requestId?: string) {\n super(message, 'INTERNAL_ERROR', 500, requestId);\n this.name = 'ServerError';\n Object.setPrototypeOf(this, ServerError.prototype);\n }\n}\n\n/**\n * Network or connection error\n */\nexport class NetworkError extends SembojaError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR', 0);\n this.name = 'NetworkError';\n Object.setPrototypeOf(this, NetworkError.prototype);\n }\n}\n","import {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from '../errors';\nimport type { ApiErrorResponse } from '../types';\n\nexport interface HttpClientOptions {\n baseUrl: string;\n apiKey: string;\n timeout: number;\n retries: number;\n}\n\nexport interface RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n body?: unknown;\n headers?: Record<string, string>;\n}\n\n/**\n * Sleep for a given number of milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Calculate exponential backoff delay\n */\nfunction getBackoffDelay(attempt: number, baseDelay = 1000): number {\n return Math.min(baseDelay * Math.pow(2, attempt), 30000);\n}\n\n/**\n * HTTP client for making API requests\n */\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeout: number;\n private readonly retries: number;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.apiKey = options.apiKey;\n this.timeout = options.timeout;\n this.retries = options.retries;\n }\n\n /**\n * Make an HTTP request with retry logic\n */\n async request<T>(options: RequestOptions): Promise<T> {\n const url = `${this.baseUrl}${options.path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n ...options.headers,\n };\n\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.retries; attempt++) {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n // Parse response\n const data = await response.json();\n\n // Handle errors\n if (!response.ok) {\n throw this.parseError(response.status, data as ApiErrorResponse);\n }\n\n return data as T;\n } catch (error) {\n lastError = error as Error;\n\n // Don't retry on client errors (4xx) except rate limits\n if (error instanceof SembojaError) {\n if (error.statusCode >= 400 && error.statusCode < 500 && error.statusCode !== 429) {\n throw error;\n }\n }\n\n // Don't retry on abort (timeout)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new NetworkError(`Request timeout after ${this.timeout}ms`);\n }\n\n // Retry on network errors and rate limits\n if (attempt < this.retries) {\n const delay = getBackoffDelay(attempt);\n await sleep(delay);\n continue;\n }\n }\n }\n\n // If we get here, all retries failed\n if (lastError instanceof SembojaError) {\n throw lastError;\n }\n throw new NetworkError(lastError?.message || 'Request failed');\n }\n\n /**\n * Parse error response into appropriate error class\n */\n private parseError(statusCode: number, data: ApiErrorResponse): SembojaError {\n const message = data.error?.message || 'Unknown error';\n const code = data.error?.code || 'UNKNOWN_ERROR';\n const requestId = data.meta?.request_id;\n\n switch (statusCode) {\n case 401:\n return new AuthenticationError(message, requestId);\n case 429:\n return new RateLimitError(message, requestId);\n case 400:\n return new ValidationError(message, requestId);\n case 404:\n return new NotFoundError(message, code, requestId);\n case 500:\n case 502:\n case 503:\n return new ServerError(message, requestId);\n default:\n return new SembojaError(message, code, statusCode, requestId);\n }\n }\n\n /**\n * GET request\n */\n async get<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'GET', path });\n }\n\n /**\n * POST request\n */\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'POST', path, body });\n }\n\n /**\n * PUT request\n */\n async put<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PUT', path, body });\n }\n\n /**\n * PATCH request\n */\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body });\n }\n\n /**\n * DELETE request\n */\n async delete<T>(path: string): Promise<T> {\n return this.request<T>({ method: 'DELETE', path });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type {\n ApiResponse,\n MessageResponseData,\n MarkAsReadResponseData,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n MarkAsReadOptions,\n} from '../types';\n\n/**\n * Messages API resource\n *\n * @example\n * ```typescript\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class Messages {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Send a text message\n *\n * @param options - Text message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello, World!',\n * previewUrl: true,\n * });\n * console.log('Message ID:', result.data.messages[0].id);\n * ```\n */\n async sendText(options: SendTextOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/text', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n text: options.text,\n preview_url: options.previewUrl,\n reply_to: options.replyTo,\n });\n }\n\n /**\n * Send a template message\n *\n * @param options - Template message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendTemplate({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * template: {\n * name: 'hello_world',\n * language: { code: 'en' },\n * },\n * });\n * ```\n */\n async sendTemplate(options: SendTemplateOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages/template', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n template: options.template,\n });\n }\n\n /**\n * Send an image message\n *\n * @param options - Image message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendImage({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * image: {\n * link: 'https://example.com/image.jpg',\n * caption: 'Check this out!',\n * },\n * });\n * ```\n */\n async sendImage(options: SendImageOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'image',\n image: options.image,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a video message\n *\n * @param options - Video message options\n * @returns Message response with message ID\n */\n async sendVideo(options: SendVideoOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'video',\n video: options.video,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send an audio message\n *\n * @param options - Audio message options\n * @returns Message response with message ID\n */\n async sendAudio(options: SendAudioOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'audio',\n audio: options.audio,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a document message\n *\n * @param options - Document message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendDocument({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * document: {\n * link: 'https://example.com/invoice.pdf',\n * filename: 'invoice.pdf',\n * caption: 'Your invoice',\n * },\n * });\n * ```\n */\n async sendDocument(options: SendDocumentOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'document',\n document: options.document,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a reaction to a message\n *\n * @param options - Reaction options\n * @returns Message response\n *\n * @example\n * ```typescript\n * // Add reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '👍',\n * },\n * });\n *\n * // Remove reaction\n * await client.messages.sendReaction({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * reaction: {\n * messageId: 'wamid.xxx',\n * emoji: '',\n * },\n * });\n * ```\n */\n async sendReaction(options: SendReactionOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'reaction',\n reaction: {\n message_id: options.reaction.messageId,\n emoji: options.reaction.emoji,\n },\n });\n }\n\n /**\n * Send an interactive message (buttons, lists, etc.)\n *\n * @param options - Interactive message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Button message\n * await client.messages.sendInteractive({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * interactive: {\n * type: 'button',\n * body: { text: 'Choose an option:' },\n * action: {\n * buttons: [\n * { type: 'reply', reply: { id: 'yes', title: 'Yes' } },\n * { type: 'reply', reply: { id: 'no', title: 'No' } },\n * ],\n * },\n * },\n * });\n * ```\n */\n async sendInteractive(\n options: SendInteractiveOptions\n ): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'interactive',\n interactive: options.interactive,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a sticker message\n *\n * @param options - Sticker message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send sticker by URL\n * const result = await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * link: 'https://example.com/sticker.webp',\n * },\n * });\n *\n * // Send sticker by media ID\n * await client.messages.sendSticker({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * sticker: {\n * id: 'media_id_here',\n * },\n * });\n * ```\n */\n async sendSticker(options: SendStickerOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'sticker',\n sticker: options.sticker,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a location message\n *\n * @param options - Location message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendLocation({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * location: {\n * latitude: -6.2088,\n * longitude: 106.8456,\n * name: 'Monas',\n * address: 'Gambir, Central Jakarta, Indonesia',\n * },\n * });\n * ```\n */\n async sendLocation(options: SendLocationOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'location',\n location: options.location,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send contact cards\n *\n * @param options - Contact message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * const result = await client.messages.sendContacts({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * contacts: [\n * {\n * name: {\n * formatted_name: 'John Doe',\n * first_name: 'John',\n * last_name: 'Doe',\n * },\n * phones: [\n * { phone: '+6281234567890', type: 'CELL' },\n * ],\n * emails: [\n * { email: 'john@example.com', type: 'WORK' },\n * ],\n * },\n * ],\n * });\n * ```\n */\n async sendContacts(options: SendContactsOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.http.post('/api/v1/messages', {\n phone_number_id: options.phoneNumberId,\n to: options.to,\n type: 'contacts',\n contacts: options.contacts,\n context: options.replyTo ? { message_id: options.replyTo } : undefined,\n });\n }\n\n /**\n * Send a WhatsApp Flow message\n *\n * WhatsApp Flows allow you to create structured, multi-screen forms\n * that run entirely inside WhatsApp. The flow must be registered via\n * the Meta Business Manager before sending.\n *\n * @param options - Flow message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a flow with navigate action (most common)\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Please fill in your company details',\n * flowId: '1234567890',\n * flowCta: 'Start Form',\n * flowToken: 'session_abc123',\n * flowAction: 'navigate',\n * screen: 'COMPANY_NAME',\n * });\n *\n * // Send a draft flow for testing\n * await client.messages.sendFlow({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Test flow',\n * flowId: '1234567890',\n * flowCta: 'Open',\n * mode: 'draft',\n * });\n * ```\n */\n async sendFlow(options: SendFlowOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'flow',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'flow',\n parameters: {\n flow_message_version: '3',\n flow_id: options.flowId,\n flow_cta: options.flowCta,\n flow_token: options.flowToken,\n flow_action: options.flowAction || 'navigate',\n flow_action_payload: options.screen\n ? {\n screen: options.screen,\n data: options.screenData,\n }\n : undefined,\n mode: options.mode,\n },\n },\n },\n });\n }\n\n /**\n * Send a CTA URL button message\n *\n * CTA URL buttons display a button that opens a URL when clicked.\n * This is ideal for payment links, external forms, or any URL you want\n * to present with a clean, branded button instead of a raw link.\n *\n * @param options - CTA URL message options\n * @returns Message response with message ID\n *\n * @example\n * ```typescript\n * // Send a payment link with a \"Pay Now\" button\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * body: 'Complete your payment securely',\n * buttonText: 'Pay Now',\n * url: 'https://checkout.stripe.com/pay/cs_xxx',\n * footer: 'Secure payment powered by Stripe',\n * });\n *\n * // Send a booking link\n * await client.messages.sendCtaUrl({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * header: 'Schedule Your Appointment',\n * body: 'Click below to book your preferred time slot',\n * buttonText: 'Book Now',\n * url: 'https://calendly.com/your-link',\n * });\n * ```\n */\n async sendCtaUrl(options: SendCtaUrlOptions): Promise<ApiResponse<MessageResponseData>> {\n return this.sendInteractive({\n phoneNumberId: options.phoneNumberId,\n to: options.to,\n replyTo: options.replyTo,\n interactive: {\n type: 'cta_url',\n header: options.header ? { type: 'text', text: options.header } : undefined,\n body: { text: options.body },\n footer: options.footer ? { text: options.footer } : undefined,\n action: {\n name: 'cta_url',\n parameters: {\n display_text: options.buttonText,\n url: options.url,\n },\n },\n },\n });\n }\n\n /**\n * Mark a message as read\n *\n * Optionally show a typing indicator after marking as read.\n * The typing indicator shows \"typing...\" in the chat for a few seconds.\n *\n * @param options - Mark as read options\n * @returns Success response\n *\n * @example\n * ```typescript\n * // Simple mark as read\n * await client.messages.markAsRead({\n * phoneNumberId: '123456789',\n * messageId: 'wamid.xxx',\n * });\n *\n * // Mark as read with typing indicator\n * await client.messages.markAsRead({\n * phoneNumberId: '123456789',\n * messageId: 'wamid.xxx',\n * typingIndicator: { type: 'text' },\n * });\n * ```\n */\n async markAsRead(options: MarkAsReadOptions): Promise<ApiResponse<MarkAsReadResponseData>> {\n return this.http.post('/api/v1/messages/read', {\n phone_number_id: options.phoneNumberId,\n message_id: options.messageId,\n typing_indicator: options.typingIndicator,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TemplateInfo, ListTemplatesOptions } from '../types';\n\n/**\n * Templates API resource\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\nexport class Templates {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all message templates\n * \n * @param options - Optional filters\n * @returns List of templates\n * \n * @example\n * ```typescript\n * const templates = await client.templates.list();\n * console.log('Templates:', templates.data);\n * \n * // Filter by status\n * const approved = await client.templates.list({ status: 'APPROVED' });\n * ```\n */\n async list(options?: ListTemplatesOptions): Promise<ApiResponse<TemplateInfo[]>> {\n let path = '/api/v1/templates';\n \n if (options?.status) {\n path += `?status=${encodeURIComponent(options.status)}`;\n }\n \n return this.http.get(path);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, PhoneNumber, PhoneNumberProfile } from '../types';\n\n/**\n * Phone Numbers API resource\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * const profile = await client.phoneNumbers.getProfile('123456');\n * ```\n */\nexport class PhoneNumbers {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * List all phone numbers configured for your account\n * \n * @returns List of phone numbers\n * \n * @example\n * ```typescript\n * const phoneNumbers = await client.phoneNumbers.list();\n * for (const phone of phoneNumbers.data) {\n * console.log(`${phone.verified_name}: ${phone.display_phone_number}`);\n * }\n * ```\n */\n async list(): Promise<ApiResponse<PhoneNumber[]>> {\n return this.http.get('/api/v1/phone-numbers');\n }\n\n /**\n * Get a specific phone number by ID\n * \n * @param id - The phone number ID (from list response)\n * @returns Phone number details\n * \n * @example\n * ```typescript\n * const phone = await client.phoneNumbers.get('phone-number-uuid');\n * console.log(phone.data.display_phone_number);\n * ```\n */\n async get(id: string): Promise<ApiResponse<PhoneNumber>> {\n return this.http.get(`/api/v1/phone-numbers/${id}`);\n }\n\n /**\n * Get phone number profile from Meta Graph API\n * \n * Returns detailed information including quality rating, verification status,\n * messaging limits, and business profile.\n * \n * @param id - The phone number ID\n * @returns Phone number profile with Meta API details\n * \n * @example\n * ```typescript\n * const profile = await client.phoneNumbers.getProfile('phone-number-uuid');\n * console.log('Quality:', profile.data.phone_number.quality_rating);\n * console.log('Name:', profile.data.business_profile.about);\n * ```\n */\n async getProfile(id: string): Promise<ApiResponse<PhoneNumberProfile>> {\n return this.http.get(`/api/v1/phone-numbers/${id}/profile`);\n }\n\n /**\n * Update business profile for a phone number\n * \n * @param id - The phone number ID\n * @param profile - Profile fields to update\n * @returns Updated business profile\n * \n * @example\n * ```typescript\n * const updated = await client.phoneNumbers.updateProfile('phone-number-uuid', {\n * about: 'We help businesses grow',\n * description: 'Customer support',\n * email: 'support@example.com',\n * });\n * ```\n */\n async updateProfile(id: string, profile: {\n about?: string;\n address?: string;\n description?: string;\n email?: string;\n vertical?: string;\n websites?: string[];\n }): Promise<ApiResponse<PhoneNumberProfile['business_profile']>> {\n return this.http.patch(`/api/v1/phone-numbers/${id}/profile`, profile);\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, UsageData } from '../types';\n\n/**\n * Usage API resource\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Sent: ${usage.data.messages.sent}`);\n * ```\n */\nexport class Usage {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get current usage statistics for the billing period\n * \n * @returns Usage statistics\n * \n * @example\n * ```typescript\n * const usage = await client.usage.get();\n * console.log(`Period: ${usage.data.period.start} - ${usage.data.period.end}`);\n * console.log(`Messages sent: ${usage.data.messages.sent}`);\n * console.log(`Messages received: ${usage.data.messages.received}`);\n * console.log(`API calls: ${usage.data.api_calls}`);\n * ```\n */\n async get(): Promise<ApiResponse<UsageData>> {\n return this.http.get('/api/v1/usage');\n }\n}\n","import type { HttpClient } from '../lib/http';\nimport type { ApiResponse, TriggerWebhookOptions } from '../types';\n\n/**\n * Test API resource (only works with sk_test_* keys)\n * \n * @example\n * ```typescript\n * // Trigger a test webhook\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Test message',\n * });\n * ```\n */\nexport class Test {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Trigger a test webhook to simulate an incoming message\n * \n * **Note:** This only works with test API keys (sk_test_*)\n * \n * @param options - Webhook trigger options\n * @returns Success response\n * \n * @example\n * ```typescript\n * await client.test.triggerWebhook({\n * phoneNumberId: '123456789',\n * type: 'text',\n * from: '+6281234567890',\n * text: 'Hello, this is a test incoming message!',\n * });\n * ```\n */\n async triggerWebhook(options: TriggerWebhookOptions): Promise<ApiResponse<{ queued: boolean }>> {\n return this.http.post('/api/v1/test/webhooks/trigger', {\n phone_number_id: options.phoneNumberId,\n type: options.type || 'text',\n from: options.from,\n text: options.text,\n });\n }\n}\n","import type { HttpClient } from '../lib/http';\n\n/**\n * Media info returned from WhatsApp API\n */\nexport interface MediaInfo {\n /** Media ID */\n id: string;\n /** Download URL (valid for 5 minutes) */\n url: string;\n /** MIME type (e.g., image/jpeg, application/pdf) */\n mime_type: string;\n /** SHA256 hash of the file */\n sha256: string;\n /** File size in bytes */\n file_size: number;\n /** Always \"whatsapp\" */\n messaging_product: 'whatsapp';\n}\n\n/**\n * Options for getting media info\n */\nexport interface GetMediaOptions {\n /** WhatsApp Business phone number ID */\n phoneNumberId: string;\n /** Media ID from incoming message */\n mediaId: string;\n}\n\n/**\n * Media API\n *\n * Retrieve and download media files sent by users.\n *\n * @example\n * ```typescript\n * // Get media info (URL valid for 5 minutes)\n * const info = await client.media.get({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * console.log(info.url, info.mime_type, info.file_size);\n *\n * // Download the actual file\n * const { data, mimeType } = await client.media.download({\n * phoneNumberId: '123456789',\n * mediaId: 'media_id_from_webhook',\n * });\n * fs.writeFileSync('file.jpg', data);\n * ```\n */\nexport class Media {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get media info including download URL\n *\n * The returned URL is only valid for 5 minutes.\n * Use `download()` to get the actual file.\n *\n * @param options - Media ID and phone number ID\n * @returns Media info with URL, mime type, and file size\n */\n async get(options: GetMediaOptions): Promise<MediaInfo> {\n const response = await this.http.request<{ success: boolean; data: MediaInfo }>({\n method: 'GET',\n path: `/api/v1/media/${options.mediaId}?phone_number_id=${options.phoneNumberId}`,\n });\n return response.data;\n }\n\n /**\n * Download media file\n *\n * Gets the media info and downloads the actual file binary.\n *\n * @param options - Media ID and phone number ID\n * @returns Buffer containing the file data and its MIME type\n */\n async download(options: GetMediaOptions): Promise<{ data: Buffer; mimeType: string }> {\n // This requires a different HTTP call that returns binary data\n // For now, we need to make a raw fetch request\n const baseUrl = (this.http as any).baseUrl || 'https://connect.semboja.tech';\n const apiKey = (this.http as any).apiKey;\n\n const response = await fetch(\n `${baseUrl}/api/v1/media/${options.mediaId}/download?phone_number_id=${options.phoneNumberId}`,\n {\n method: 'GET',\n headers: {\n 'X-API-Key': apiKey,\n },\n }\n );\n\n if (!response.ok) {\n const errorData = (await response\n .json()\n .catch(() => ({ error: { message: response.statusText } }))) as {\n error?: { message?: string };\n };\n throw new Error(errorData.error?.message || `Download failed: ${response.status}`);\n }\n\n const data = Buffer.from(await response.arrayBuffer());\n const mimeType = response.headers.get('content-type') || 'application/octet-stream';\n\n return { data, mimeType };\n }\n}\n","import { HttpClient } from './lib/http';\nimport { Messages } from './resources/messages';\nimport { Templates } from './resources/templates';\nimport { PhoneNumbers } from './resources/phone-numbers';\nimport { Usage } from './resources/usage';\nimport { Test } from './resources/test';\nimport { Media } from './resources/media';\nimport type { ClientOptions } from './types';\n\nconst DEFAULT_BASE_URL = 'https://connect.semboja.tech';\nconst DEFAULT_TIMEOUT = 30000;\nconst DEFAULT_RETRIES = 3;\n\n/**\n * Semboja WhatsApp API Client\n *\n * @example\n * ```typescript\n * import { SembojaClient } from '@semboja/connect';\n *\n * // Simple initialization\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // With options\n * const client = new SembojaClient({\n * apiKey: process.env.SEMBOJA_API_KEY!,\n * timeout: 60000,\n * retries: 5,\n * });\n *\n * // Send a message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n * ```\n */\nexport class SembojaClient {\n /** Messages API */\n readonly messages: Messages;\n\n /** Templates API */\n readonly templates: Templates;\n\n /** Phone Numbers API */\n readonly phoneNumbers: PhoneNumbers;\n\n /** Usage API */\n readonly usage: Usage;\n\n /** Test API (only works with sk_test_* keys) */\n readonly test: Test;\n\n /** Media API (retrieve uploaded files) */\n readonly media: Media;\n\n /** Whether the client is in test mode */\n readonly isTestMode: boolean;\n\n private readonly http: HttpClient;\n\n /**\n * Create a new Semboja client\n *\n * @param optionsOrApiKey - API key string or client options object\n *\n * @example\n * ```typescript\n * // Using API key directly\n * const client = new SembojaClient('sk_live_xxx');\n *\n * // Using options object\n * const client = new SembojaClient({\n * apiKey: 'sk_live_xxx',\n * timeout: 60000,\n * retries: 5,\n * });\n * ```\n */\n constructor(optionsOrApiKey: string | ClientOptions) {\n const options: ClientOptions =\n typeof optionsOrApiKey === 'string' ? { apiKey: optionsOrApiKey } : optionsOrApiKey;\n\n // Validate API key\n if (!options.apiKey) {\n throw new Error('API key is required');\n }\n\n if (!options.apiKey.startsWith('sk_live_') && !options.apiKey.startsWith('sk_test_')) {\n throw new Error('Invalid API key format. Must start with sk_live_ or sk_test_');\n }\n\n // Determine if test mode\n this.isTestMode = options.apiKey.startsWith('sk_test_');\n\n // Create HTTP client\n this.http = new HttpClient({\n baseUrl: options.baseUrl || DEFAULT_BASE_URL,\n apiKey: options.apiKey,\n timeout: options.timeout || DEFAULT_TIMEOUT,\n retries: options.retries ?? DEFAULT_RETRIES,\n });\n\n // Initialize resources\n this.messages = new Messages(this.http);\n this.templates = new Templates(this.http);\n this.phoneNumbers = new PhoneNumbers(this.http);\n this.usage = new Usage(this.http);\n this.test = new Test(this.http);\n this.media = new Media(this.http);\n }\n}\n","import { createHmac, timingSafeEqual } from 'crypto';\nimport type { VerifyWebhookOptions, FlowResponseData } from '../types';\n\n/**\n * Verify a webhook signature from Semboja\n *\n * @param options - Verification options\n * @returns true if the signature is valid, false otherwise\n *\n * @example\n * ```typescript\n * import { verifyWebhookSignature } from '@semboja/connect';\n *\n * // Express.js example\n * app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'] as string,\n * timestamp: req.headers['x-semboja-timestamp'] as string,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return res.status(401).send('Invalid signature');\n * }\n *\n * // Process the webhook\n * const event = JSON.parse(req.body.toString());\n * console.log('Received event:', event);\n *\n * res.status(200).send('OK');\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Hono example\n * app.post('/webhook', async (c) => {\n * const body = await c.req.text();\n * const signature = c.req.header('x-semboja-signature');\n * const timestamp = c.req.header('x-semboja-timestamp');\n *\n * const isValid = verifyWebhookSignature({\n * payload: body,\n * signature: signature!,\n * timestamp: timestamp!,\n * secret: process.env.WEBHOOK_SECRET!,\n * });\n *\n * if (!isValid) {\n * return c.text('Invalid signature', 401);\n * }\n *\n * const event = JSON.parse(body);\n * // Process the webhook...\n *\n * return c.text('OK');\n * });\n * ```\n */\nexport function verifyWebhookSignature(options: VerifyWebhookOptions): boolean {\n const { payload, signature, timestamp, secret } = options;\n\n // Validate inputs\n if (!payload || !signature || !timestamp || !secret) {\n return false;\n }\n\n // Check timestamp to prevent replay attacks (5 minute tolerance)\n const timestampNum = parseInt(timestamp, 10);\n const now = Math.floor(Date.now() / 1000);\n const tolerance = 5 * 60; // 5 minutes\n\n if (isNaN(timestampNum) || Math.abs(now - timestampNum) > tolerance) {\n return false;\n }\n\n // Stringify payload if it's an object\n const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);\n\n // Compute expected signature (format: timestamp.payload)\n const signatureData = `${timestamp}.${payloadString}`;\n const expectedSignature =\n 'sha256=' + createHmac('sha256', secret).update(signatureData).digest('hex');\n\n // Compare signatures using timing-safe comparison\n try {\n const sigBuffer = Buffer.from(signature);\n const expectedBuffer = Buffer.from(expectedSignature);\n\n if (sigBuffer.length !== expectedBuffer.length) {\n return false;\n }\n\n return timingSafeEqual(sigBuffer, expectedBuffer);\n } catch {\n return false;\n }\n}\n\n/**\n * Parse a webhook payload into a typed event object\n *\n * @param payload - Raw webhook payload (string or object)\n * @returns Parsed webhook event\n */\nexport function parseWebhookPayload<T = unknown>(payload: string | object): T {\n if (typeof payload === 'string') {\n return JSON.parse(payload) as T;\n }\n return payload as T;\n}\n\n/**\n * Parse the response_json string from a WhatsApp Flow nfm_reply webhook.\n *\n * When a user completes a WhatsApp Flow, the webhook delivers an interactive\n * message with type 'nfm_reply'. The `response_json` field contains a stringified\n * JSON object with the `flow_token` and all data collected by the flow screens.\n *\n * @param responseJson - The response_json string from nfm_reply\n * @returns Parsed flow response data including flow_token and collected fields\n *\n * @example\n * ```typescript\n * import { parseFlowResponse } from '@semboja/connect';\n *\n * // In your webhook handler:\n * if (message.interactive?.type === 'nfm_reply') {\n * const flowData = parseFlowResponse(message.interactive.nfm_reply.response_json);\n * console.log('Flow token:', flowData.flow_token);\n * console.log('Company name:', flowData.company_name);\n * }\n * ```\n */\nexport function parseFlowResponse(responseJson: string): FlowResponseData {\n return JSON.parse(responseJson) as FlowResponseData;\n}\n","/**\n * @semboja/connect - Official Node.js SDK for Semboja WhatsApp API Bridge\n *\n * @example\n * ```typescript\n * // Using default import\n * import Semboja from '@semboja/connect';\n * const client = new Semboja('sk_live_your_api_key');\n *\n * // Or using named import\n * import { SembojaClient, verifyWebhookSignature } from '@semboja/connect';\n * const client = new SembojaClient('sk_live_your_api_key');\n *\n * // Send a text message\n * await client.messages.sendText({\n * phoneNumberId: '123456789',\n * to: '+6281234567890',\n * text: 'Hello from Semboja!',\n * });\n *\n * // Verify webhook signature\n * const isValid = verifyWebhookSignature({\n * payload: req.body,\n * signature: req.headers['x-semboja-signature'],\n * timestamp: req.headers['x-semboja-timestamp'],\n * secret: process.env.WEBHOOK_SECRET,\n * });\n * ```\n *\n * @packageDocumentation\n */\n\n// Main client\nexport { SembojaClient } from './client';\n\n// Default export for convenience\nimport { SembojaClient as Client } from './client';\nexport default Client;\n\n// Webhook utilities\nexport { verifyWebhookSignature, parseWebhookPayload, parseFlowResponse } from './webhooks/verify';\n\n// Error classes\nexport {\n SembojaError,\n AuthenticationError,\n RateLimitError,\n ValidationError,\n NotFoundError,\n ServerError,\n NetworkError,\n} from './errors';\n\n// Types\nexport type {\n // Client\n ClientOptions,\n ApiResponse,\n ApiErrorResponse,\n\n // Messages\n MessageType,\n SendMessageOptions,\n SendTextOptions,\n SendTemplateOptions,\n SendImageOptions,\n SendVideoOptions,\n SendAudioOptions,\n SendDocumentOptions,\n SendStickerOptions,\n SendLocationOptions,\n SendContactsOptions,\n SendReactionOptions,\n SendInteractiveOptions,\n SendFlowOptions,\n SendCtaUrlOptions,\n FlowActionParameters,\n CtaUrlActionParameters,\n MarkAsReadOptions,\n TypingIndicator,\n Template,\n TemplateLanguage,\n TemplateComponent,\n TemplateParameter,\n MediaObject,\n StickerObject,\n LocationObject,\n Contact,\n ContactName,\n ContactPhone,\n ContactEmail,\n ContactAddress,\n ContactUrl,\n ContactOrg,\n InteractiveMessage,\n InteractiveButton,\n InteractiveListSection,\n MessageResponseData,\n MarkAsReadResponseData,\n MessageContact,\n MessageResult,\n\n // Templates\n TemplateStatus,\n TemplateInfo,\n ListTemplatesOptions,\n\n // Phone Numbers\n PhoneNumber,\n PhoneNumberProfile,\n MetaPhoneNumberInfo,\n MetaBusinessProfile,\n\n // Usage\n UsageData,\n UsagePeriod,\n UsageMessages,\n\n // Test\n TriggerWebhookOptions,\n\n // Webhooks\n VerifyWebhookOptions,\n WebhookInteractiveReply,\n FlowResponseData,\n} from './types';\n\n// Media types\nexport type { MediaInfo, GetMediaOptions } from './resources/media';\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semboja/connect",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Official Node.js SDK for Semboja WhatsApp API Bridge",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",