UNPKG

@great-detail/whatsapp

Version:

SDK for interfacing with WhatsApp Business Platform in Typescript or Node.js using the Cloud API, hosted by Meta.

1,625 lines (1,573 loc) 52.7 kB
import * as ky from 'ky'; import { KyInstance, Options as Options$1 } from 'ky'; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ /** * A Phone Number. **Not** a Phone Number ID. */ type PhoneNumberString = `+${string}` | (string & NonNullable<unknown>); /** * WhatsApp Phone Number ID. * * @since 7.0.0 */ type PhoneNumberID = string; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type GetBusinessProfileFields = { about?: boolean; address?: boolean; description?: boolean; email?: boolean; profile_picture_url?: boolean; websites?: boolean; vertical?: boolean; }; type GetBusinessProfileOptions<Fields extends GetBusinessProfileFields = object> = { phoneNumberID: PhoneNumberID; fields?: Fields; }; type GetBusinessProfilePayload<Fields extends GetBusinessProfileFields = object> = { data: [ { about: Fields extends { about: true; } ? string : undefined; address: Fields extends { address: true; } ? string : undefined; description: Fields extends { description: true; } ? string : undefined; email: Fields extends { email: true; } ? string : undefined; messaging_product: "whatsapp"; profile_picture_url: Fields extends { profile_picture_url: true; } ? string : undefined; vertical: Fields extends { vertical: true; } ? "" | (string & NonNullable<unknown>) : undefined; websites: Fields extends { websites: true; } ? [string] | [string, string] | (string[] & NonNullable<unknown>) : undefined; } ]; }; type UpdateBusinessProfileOptions = { phoneNumberID: PhoneNumberID; about?: string; address?: string; description?: string; email?: string; profile_picture_handle?: string; vertical?: "" | (string & NonNullable<unknown>); websites?: [string] | [string, string] | (string[] & NonNullable<unknown>); }; type UpdateBusinessProfilePayload = { success: boolean; }; interface MethodOptions$4 { request?: Options$1; } declare class BusinessProfile { protected _transport: KyInstance; constructor(_transport: KyInstance); protected getEndpoint(phoneNumberID: PhoneNumberID): string; getBusinessProfile<Fields extends GetBusinessProfileFields = object>({ phoneNumberID, fields, request, }: MethodOptions$4 & GetBusinessProfileOptions<Fields>): ky.ResponsePromise<GetBusinessProfilePayload<Fields>>; updateBusinessProfile({ phoneNumberID, request, ...json }: MethodOptions$4 & UpdateBusinessProfileOptions): ky.ResponsePromise<UpdateBusinessProfilePayload>; } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type MediaID = string; interface MediaUploadOptions { file: Blob; phoneNumberID: PhoneNumberID; filename?: string; mimeType: string; } type MediaUploadPayload = { id: MediaID; }; type MediaGetURLOptions = { phoneNumberID?: PhoneNumberID; mediaID: MediaID; }; type MediaGetURLPayload = { messaging_product: "whatsapp"; url: string; mime_type: string; sha256: string; file_size: string | number; id: MediaID; }; type MediaDeleteOptions = { phoneNumberID?: PhoneNumberID; mediaID: MediaID; }; type MediaDeletePayload = { success: boolean; }; interface MethodOptions$3 { request?: Options$1; } interface DownloadOptions { mediaURL: string; } declare class Media { protected _transport: KyInstance; constructor(_transport: KyInstance); protected getEndpoint(phoneNumberID: PhoneNumberID): string; /** * Upload Media. * All media files sent through this endpoint are encrypted and persist for * 30 days, unless they are deleted earlier. */ upload({ phoneNumberID, file, filename, mimeType, request, }: MethodOptions$3 & MediaUploadOptions): ky.ResponsePromise<MediaUploadPayload>; /** * Retrieve Media URL. * Use the returned URL to download the media file. Note that clicking this * URL (i.e. performing a generic GET) will not return the media; you must * include an access token. * * A successful response includes an object with a media url. The URL is only * valid for 5 minutes. */ getURL({ mediaID, phoneNumberID, request, }: MethodOptions$3 & MediaGetURLOptions): ky.ResponsePromise<MediaGetURLPayload>; delete({ mediaID, phoneNumberID, request, }: MethodOptions$3 & MediaDeleteOptions): ky.ResponsePromise<MediaDeletePayload>; /** * Download Media. * All media URLs expire after 5 minutes —you need to retrieve the media URL * again if it expires. If you directly click on the URL you get from a * `/MEDIA_ID` GET call, you get an access error. * * If successful, you will receive the binary data of media saved in * media_file, response headers contain a content-type header to indicate the * mime type of returned data. * * If media fails to download, you will receive a `404 Not Found` response * code. In that case, we recommend you try to retrieve a new media URL and * download it again. If doing so doesn't resolve the issue, please try to * renew the `ACCESS_TOKEN` then retry downloading the media. * * @see {@link https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#supported-media-types} * @example * // Download Media and Write to a File * const download = await sdk.media.download({ mediaURL }); * const file = await download.arrayBuffer(); * fs.writeFile("filename.ext", Buffer.from(file)); */ download({ mediaURL, request }: MethodOptions$3 & DownloadOptions): ky.ResponsePromise<unknown>; } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ /** * A WhatsApp End-User Account ID. */ type AccountID = string; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type WhatsappError = { code: number; message?: string; type: string; title?: string; error_data?: { messaging_product: "whatsapp"; /** * Error description and a description of the most likely reason for the * error. May also contain information on how to address the error, such as * which parameter is invalid or what values are acceptable. */ details: string; }; /** * Trace ID you can include when contacting Direct Support. The ID may help * support to debug the error. */ fbtrace_id: string; /** * @deprecated Since Graph API v16.0. */ error_subcode?: number; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type CreateMessageContact = { /** * Full contact address(es) formatted as an addresses object. */ addresses: { type: "HOME" | "WORK" | (string & NonNullable<unknown>); /** * Street number and name. * * @since 4.2.0 */ street?: string; /** * City name. * * @since 4.2.0 */ city?: string; /** * State abbreviation. * * @since 4.2.0 */ state?: string; /** * ZIP code. * * @since 4.2.0 */ zip?: string; /** * Full country name. * * @since 4.2.0 */ country?: string; /** * Two-letter country abbreviation. * * @since 4.2.0 */ country_code?: string; }[]; /** * YYYY-MM-DD formatted string. */ birthday?: `${number}${number}${number}${number}-${number}${number}-${number}${number}` | (string & NonNullable<unknown>); /** * Contact email address(es) formatted as an emails object. */ emails: { type: "HOME" | "WORK" | (string & NonNullable<unknown>); /** * Email address. */ email?: string; }[]; /** * Full contact name formatted as a name object. * This appears to fit the required fields pattern for VCF4 contact files. */ name: { /** * Full name, as it normally appears. */ formatted_name: string; /** * First name. */ first_name?: string; /** * Last name. * * @since 4.2.0 */ last_name?: string; /** * Middle name. * * @since 4.2.0 */ middle_name?: string; /** * Name suffix. * * @since 4.2.0 */ suffix?: string; /** * Name prefix. * * @since 4.2.0 */ prefix?: string; }; org?: { /** * Name of the contact's company. */ company?: string; /** * Name of the contact's department. */ department?: string; /** * Contact's business title. */ title?: string; }; phones?: { type?: "CELL" | "MAIN" | "IPHONE" | "HOME" | "WORK" | (string & NonNullable<unknown>); /** * Automatically populated with the `wa_id` value as a formatted phone * number. */ phone?: PhoneNumberString; /** * WhatsApp ID. */ wa_id?: AccountID; }[]; urls: { type?: "HOME" | "WORK" | (string & NonNullable<unknown>); /** * URL. * * @since 4.2.0 */ url?: string; }[]; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type CreateMessageLocation = { /** * Longitude of the location. */ longitude: number; /** * Latitude of the location. */ latitude: number; /** * Name of the location. */ name: string; /** * Address of the location. */ address: string; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type CreateMessageMedia = { /** * ID for the media file. */ id?: string; /** * Required when type is audio, document, image, sticker, or video and you * are not using an uploaded media ID (i.e. you are hosting the media asset * on your public server). * * The protocol and URL of the media to be sent. Use only with HTTP/HTTPS URLs. * * Do not use this field when message type is set to text. * * Cloud API users only: * * - See {@link https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages#media-http-caching} * if you would like us to cache the media asset for future messages. * - When we request the media asset from your server you must indicate the * media's MIME type by including the `Content-Type` HTTP header. For * example: `Content-Type: video/mp4`. See * {@link https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#supported-media-types} * for a list of supported media and their MIME types. */ link?: string; /** * Media asset caption. Do not use with audio or sticker media. * * On-Premises API users: * * - For v2.41.2 or newer, this field is limited to 1024 characters. * - Captions are currently not supported for `document` media. */ caption?: string; /** * Describes the filename for the specific document. Use only with document * media. * * The extension of the filename will specify what format the document is * displayed as in WhatsApp. */ filename?: string; }; type EventNotificationMessageMedia = { /** * ID for the media file. * * @since 5.0.0 */ id?: string; /** * Mime type of the media file. * * @since 5.0.0 */ mime_type: string; /** * Media asset caption. Do not use with audio or sticker media. * * On-Premises API users: * * - For v2.41.2 or newer, this field is limited to 1024 characters. * - Captions are currently not supported for `document` media. */ caption?: string; /** * Describes the filename for the specific document. Use only with document * media. * * The extension of the filename will specify what format the document is * displayed as in WhatsApp. */ filename?: string; /** * Image hash. */ sha256: string; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare enum MessageType { Audio = "audio",// for audio messages. Contacts = "contacts",// for contact messages. Document = "document",// for document messages. Image = "image",// for image messages. Interactive = "interactive",// for list and reply button messages. Location = "location",// for location messages. Reaction = "sticker",// for reaction messages. Sticker = "sticker",// for sticker messages. /** * Not supported for Outgoing Messages. */ System = "system", /** * Not supported for Outgoing Messages. */ Button = "button", /** * Not supported for Outgoing Messages. */ Order = "order", Template = "template",// for template messages. Text and media (images and documents) message templates are supported. Text = "text",// for text messages. Video = "video" } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type MessageTemplateComponentNamedParameter = { parameter_name: string; }; type MessageTemplateComponentPositionalParameter = { parameter_name?: never; }; type MessageTemplateButtonTextParameter<T> = { type: "text"; /** * Developer-defined payload that is returned when the button is clicked in addition to the display text on the button. * * Required for `quick_reply` buttons. */ payload?: string; text?: string; } & T; type MessageTemplateButtonPayloadParameter<T> = { type: "payload"; /** * Developer-defined payload that is returned when the button is clicked in addition to the display text on the button. * * Required for `quick_reply` buttons. */ payload?: string; /** * Developer-provided suffix that is appended to the predefined prefix URL in the template. * * Required for URL buttons. */ text?: string; } & T; type MessageTemplateTextParameter<T> = { type: "text"; /** * The message’s text. Character limit varies based on the following included component type. * * For the header component type: * - 60 characters * * For the body component type: * - 1024 characters if other component types are included * - 32768 characters if body is the only component type included */ text: string; } & T; type MessageTemplateImageParameter<T> = { type: MessageType.Image; [MessageType.Image]: Omit<CreateMessageMedia, "caption">; } & T; type MessageTemplateDocumentParameter<T> = { type: MessageType.Document; [MessageType.Document]: Omit<CreateMessageMedia, "caption">; } & T; type MessageTemplateVideoParameter<T> = { type: MessageType.Video; [MessageType.Video]: Omit<CreateMessageMedia, "caption">; } & T; type MessageTemplateCurrencyParameter<T> = { type: "currency"; currency: { /** Default text if localization fails. */ fallback_value: string; /** Currency code as defined in ISO 4217. */ code: string; /** Amount multiplied by 1000. */ amount_1000: number; }; } & T; type MessageTemplateDateTimeParameter<T> = { type: "date_time"; date_time: { /** Default text. For Cloud API, we always use the fallback value, and we do not attempt to localize using other optional fields. */ fallback_value: string; }; } & T; type MessageTemplateHeaderLocationParameter<T> = { type: "location"; location: { latitude: string; longitude: string; name: string; address: string; }; } & T; type MessageTemplateBodyParameter<T> = MessageTemplateTextParameter<T> | MessageTemplateImageParameter<T> | MessageTemplateDocumentParameter<T> | MessageTemplateVideoParameter<T> | MessageTemplateCurrencyParameter<T> | MessageTemplateDateTimeParameter<T>; type MessageTemplateHeaderParameter<T> = MessageTemplateTextParameter<T> | MessageTemplateImageParameter<T> | MessageTemplateDocumentParameter<T> | MessageTemplateVideoParameter<T> | MessageTemplateCurrencyParameter<T> | MessageTemplateDateTimeParameter<T> | MessageTemplateHeaderLocationParameter<T>; type MessageTemplateButtonParameter<T> = MessageTemplateButtonPayloadParameter<T> | MessageTemplateButtonTextParameter<T>; type HeaderMessageComponent<T> = { type: "header"; parameters: MessageTemplateHeaderParameter<T>[]; }; type BodyMessageComponent<T> = { type: "body"; parameters: MessageTemplateBodyParameter<T>[]; }; type ButtonMessageComponent<T> = { type: "button"; /** Numeric string */ index: "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; } & ({ sub_type: "quick_reply"; parameters: (MessageTemplateButtonParameter<T> & { payload: Required<MessageTemplateButtonParameter<T>["payload"]>; })[]; } | { sub_type: "url"; parameters: (MessageTemplateButtonParameter<T> & { text: Required<MessageTemplateButtonParameter<T>["text"]>; })[]; }); type MessageComponent<T> = HeaderMessageComponent<T> | BodyMessageComponent<T> | ButtonMessageComponent<T>; type CreateMessageTemplate = { /** Name of the template. */ name: string; /** Specifies the language the template may be rendered in. */ language: { /** * The language policy the message should follow. * * @see https://developers.facebook.com/docs/whatsapp/api/messages/message-templates#language-policy-options */ policy: "deterministic"; /** * The code of the language or locale to use. * * Accepts both language and language_locale formats (e.g., en and en_US). * * @see https://developers.facebook.com/docs/whatsapp/api/messages/message-templates#supported-languages */ code: string; }; /** Array of components objects containing the parameters of the message. */ components: MessageComponent<MessageTemplateComponentNamedParameter>[] | MessageComponent<MessageTemplateComponentPositionalParameter>[]; /** * Namespace of the template. * * @deprecated Used by On-Premises API Only */ namespace?: string; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type CreateMessageText = { body: string; preview_url?: boolean; }; type EventNotificationMessageText = { body: string; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ /** * WhatsApp Message ID. * This ID likely starts with `wamid.`. */ type MessageID = `wamid.${string}` | (string & NonNullable<unknown>); type MessageStatusType = "read" | (string & NonNullable<unknown>); type MessageRecipientType = "individual" | "group" | (string & NonNullable<unknown>); type CreateMessageOptions = { phoneNumberID: PhoneNumberID; /** * WhatsApp ID or phone number of the customer you want to send a message to. */ to: PhoneNumberID | PhoneNumberString; /** * Currently, you can only send messages to individuals. * * @default "individual" * @see {@link https://developers.facebook.com/docs/graph-api/reference/whats-app-business-account-to-number-current-status/messages/#parameters} */ recipientType?: MessageRecipientType; /** * Required if replying to any message in the conversation. */ context?: { inReplyTo: MessageID; }; /** * An arbitrary 256B string, useful for tracking. For example, you could pass * the message template ID in this field to track your customer's journey * starting from the first message you send. You could then track the ROI of * different message template types to determine the most effective one. * * Any app subscribed to the messages webhook field on the WhatsApp Business * Account can get this string, as it is included in statuses object within * webhook payloads. * * Cloud API does not process this field, it just returns it as part of * sent/delivered/read message webhooks. * * @since November 14, 2023 */ biz_opaque_callback_data?: string; [key: string]: unknown | undefined; } & ({ type: MessageType.Audio; [MessageType.Audio]: Omit<CreateMessageMedia, "caption">; } | { type: MessageType.Contacts; [MessageType.Contacts]: CreateMessageContact[]; } | { type: MessageType.Document; [MessageType.Document]: CreateMessageMedia; } | { type: MessageType.Image; [MessageType.Image]: CreateMessageMedia; } | { type: MessageType.Interactive; [MessageType.Interactive]: unknown; } | { type: MessageType.Location; [MessageType.Location]: CreateMessageLocation; } | { type: MessageType.Reaction; [MessageType.Reaction]: Omit<CreateMessageMedia, "caption">; } | { type: MessageType.Sticker; [MessageType.Sticker]: Omit<CreateMessageMedia, "caption">; } | { type: MessageType.Template; [MessageType.Template]: CreateMessageTemplate; } | { type: MessageType.Text; [MessageType.Text]: CreateMessageText; } | { type: MessageType.Video; [MessageType.Video]: CreateMessageMedia; } | { type: string & NonNullable<unknown>; }); type CreateMessagePayload = { messaging_product: "whatsapp"; contacts: { input: PhoneNumberString; wa_id: AccountID; }[]; messages: { id: MessageID; message_status: "accepted" | "held_for_quality_assessment"; }[]; error: WhatsappError; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type CreateStatusOptions = { phoneNumberID: PhoneNumberID; message_id: MessageID; status: MessageStatusType; typing_indicator?: { type: "text"; }; [key: string]: unknown | undefined; }; type CreateStatusPayload = { success?: boolean; error: WhatsappError; }; interface MethodOptions$2 { request?: Options$1; } declare class Message { protected _transport: KyInstance; constructor(_transport: KyInstance); protected getEndpoint(phoneNumberID: PhoneNumberID): string; /** * Create a Status Message. * * ```ts * const message = await sdk.message.createStatus({ * phoneNumberID: "123...809", * message_id: "...", * status: "read", * typing_indicator: { type: "text" }, * }); * ``` */ createStatus({ phoneNumberID, request, ...status }: MethodOptions$2 & CreateStatusOptions): ky.ResponsePromise<CreateStatusPayload>; /** * Create a Message. * * ```ts * const message = await sdk.message.createMessage({ * phoneNumberID: "123...809", * to: "1234567890", * type: "text", * text: { body: "Hello" }, * }); * ``` */ createMessage({ to, phoneNumberID, context, recipientType, request, ...message }: MethodOptions$2 & CreateMessageOptions): ky.ResponsePromise<CreateMessagePayload>; } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type BusinessAccountID = string; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type ListPhoneNumbersOptions = { businessAccountID: BusinessAccountID; sort?: string; filtering?: string; }; type ListPhoneNumbersPaylod = { data: { id: PhoneNumberID; verified_name: string; code_verification_status: "VERIFIED" | (string & NonNullable<unknown>); display_phone_number: string; quality_rating: string; platform_type: string; throughput: { level: string; }; last_onboarded_time?: string; webhook_configuration: { application?: string; }; }[]; }; type GetPhoneNumberFields = { name_status?: boolean; }; type GetPhoneNumberOptions<Fields extends GetPhoneNumberFields = object> = { phoneNumberID: PhoneNumberID; fields?: Fields; }; type GetPhoneNumberPayload<Fields extends GetPhoneNumberFields = object> = { id: PhoneNumberID; verified_name: string; name_status: Fields extends { name_status: true; } ? "APPROVED" | "AVAILABLE_WITHOUT_REVIEW" | "DECLINED" | "EXPIRED" | "PENDING_REVIEW" | "NONE" | (string & NonNullable<unknown>) : undefined; code_verification_status: "VERIFIED" | (string & NonNullable<unknown>); display_phone_number: string; quality_rating: string; platform_type: string; throughput: { level: string; }; last_onboarded_time?: string; webhook_configuration: { application?: string; }; }; interface MethodOptions$1 { request?: Options$1; } declare class PhoneNumbers { protected _transport: KyInstance; constructor(_transport: KyInstance); getEndpoint(businessAccountID: BusinessAccountID): string; getPhoneNumber<Fields extends GetPhoneNumberFields = object>({ phoneNumberID, fields, request, }: MethodOptions$1 & GetPhoneNumberOptions): ky.ResponsePromise<GetPhoneNumberPayload<Fields>>; listPhoneNumbers({ businessAccountID, sort, filtering, request, }: MethodOptions$1 & ListPhoneNumbersOptions): ky.ResponsePromise<ListPhoneNumbersPaylod>; } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type CreateSubscriptionOptions = { businessAccountID: BusinessAccountID; }; type CreateSubscriptionPayload = { success: boolean; }; type ListSubscriptionsOptions = { businessAccountID: BusinessAccountID; }; type ListSubscriptionsPayload = { data: { whatsapp_business_api_data: { id: string; link?: string | null; name: string; }; }[]; }; interface MethodOptions { request?: Options$1; } declare class SubscribedApps { protected _transport: KyInstance; constructor(_transport: KyInstance); protected getEndpoint(businessAccountID: BusinessAccountID): string; createSubscription({ businessAccountID, request, }: MethodOptions & CreateSubscriptionOptions): ky.ResponsePromise<CreateSubscriptionPayload>; listSubscriptions({ businessAccountID, request, }: MethodOptions & ListSubscriptionsOptions): ky.ResponsePromise<ListSubscriptionsPayload>; } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type EventNotificationMessageIdentity = { /** * State of acknowledgment for the messages system customer_identity_changed. */ acknowledged: string; /** * The time when the WhatsApp Business Management API detected the customer * may have changed their profile information. */ created_timestamp: string; /** * The ID for the messages system customer_identity_changed. */ hash: string; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type EventNotificationMessageReferral = { /** * The Meta URL that leads to the ad or post clicked by the customer. Opening * this URL takes you to the ad viewed by your customer. */ source_url: string; /** * The type of the ad's source. */ source_type: "ad" | "post" | (string & NonNullable<unknown>); /** * Meta ID for an ad or a post. */ source_id: string; /** * Headline used in the ad or post. */ headline: string; /** * Body for the ad or post. */ body: string; /** * Media present in the ad or post. */ media_type: "image" | "video" | (string & NonNullable<unknown>); /** * URL of the image, when media_type is an image. */ image_url: URL; /** * URL of the video, when media_type is a video. */ video_url: URL; /** * URL for the thumbnail, when media_type is a video. */ thumbnail_url: URL; /** * Click ID generated by Meta for ads that click to WhatsApp. * * @since September 27, 2023 */ ctwa_clid?: string; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type EventNotificationMessageSystem = { /** * Describes the change to the customer's identity or phone number. */ body: string; /** * Hash for the identity fetched from server. */ identity: string; /** * The WhatsApp ID for the customer prior to the update. */ customer: AccountID; /** * Type of system update. */ type: "customer_changed_number" | "customer_identity_changed.js"; /** * New WhatsApp ID for the customer when their phone number is updated. * * @deprecated since Webhook v11.0 */ new_wa_id: AccountID; /** * New WhatsApp ID for the customer when their phone number is updated. */ wa_id: AccountID; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ type ConversationType = "authentication" | "marketing" | "utility" | "service" | "referral_conversation" | (string & NonNullable<unknown>); type WebhookEventNotificationMessagesChanges = { /** * Notification type. Value will be messages. */ field: "messages"; /** * A value object. */ value: { /** * Array of contact objects with information for the customer who sent a * message to the business. */ contacts: { /** * The customer's WhatsApp ID. A business can respond to a customer using * this ID. This ID may not match the customer's phone number, which is * returned by the API as input when sending a message to the customer. */ wa_id: AccountID; /** * Additional unique, alphanumeric identifier for a WhatsApp user. */ user_id?: string; /** * A customer profile object. */ profile: { /** * The customer's name. */ name: string; }; }[]; /** * An array of error objects describing the error. */ errors: WhatsappError[]; /** * Product used to send the message. Value is always whatsapp. * * @default 'whatsapp' */ messaging_product: "whatsapp"; /** * Information about a message received by the business that is subscribed to * the webhook. */ messages?: ({ /** * The ID for the message that was received by the business. You could use * messages endpoint to mark this specific message as read. */ id: MessageID; /** * Unix timestamp indicating when the WhatsApp server received the message * from the customer. */ timestamp: string; /** * The customer's WhatsApp ID. A business can respond to a customer using * this ID. This ID may not match the customer's phone number, which is * returned by the API as input when sending a message to the customer. */ from: AccountID; /** * Context object. Only included when a user replies or interacts with one of * your messages. */ context?: { /** * Set to true if the message received by the business has been forwarded. */ forwarded: boolean; /** * Set to true if the message received by the business has been forwarded * more than 5 times. */ frequently_forwarded: boolean; /** * The WhatsApp ID for the customer who replied to an inbound message. */ from: AccountID; /** * The message ID for the sent message for an inbound reply. */ id: MessageID; /** * Referred product object describing the product the user is requesting * information about. You must parse this value if you support Product * Enquiry Messages. */ referred_product: { /** * Unique identifier of the Meta catalog linked to the WhatsApp Business * Account. */ catalog_id: string; /** * Unique identifier of the product in a catalog. */ product_retailer_id: string; }; }; /** * An array of error objects describing the error. * * @since 4.2.0 */ errors?: WhatsappError[]; /** * An identity object. Webhook is triggered when a customer's phone number or * profile information has been updated. */ identity: EventNotificationMessageIdentity; /** * Referral object. When a customer clicks an ad that redirects to WhatsApp, * this object is included in the messages object. */ referral?: EventNotificationMessageReferral; } & ({ type: MessageType.Audio; [MessageType.Audio]: Omit<EventNotificationMessageMedia, "caption" | "filename" | "sha256">; } | { type: MessageType.Button; [MessageType.Button]: { payload: string; text: string; }; } | { type: MessageType.Contacts; [MessageType.Contacts]: unknown; } | { type: MessageType.Document; [MessageType.Document]: EventNotificationMessageMedia; } | { type: MessageType.Image; [MessageType.Image]: EventNotificationMessageMedia; } | { type: MessageType.Interactive; [MessageType.Interactive]: unknown; } | { type: MessageType.Order; [MessageType.Order]: unknown; } | { type: MessageType.Location; [MessageType.Location]: unknown; } | { type: MessageType.Reaction | MessageType.Sticker; [MessageType.Sticker]: Omit<EventNotificationMessageMedia, "caption" | "filename"> & { animated: boolean; }; } | { type: MessageType.Text; [MessageType.Text]: EventNotificationMessageText; } | { type: MessageType.Video; [MessageType.Video]: Omit<EventNotificationMessageMedia, "filename">; } | { type: MessageType.System; /** * When messages type is set to system, a customer has updated their phone * number or profile information, this object is included in the messages * object. */ [MessageType.System]: EventNotificationMessageSystem; }))[]; /** * A metadata object describing the business subscribed to the webhook. */ metadata: { /** * The phone number that is displayed for a business. */ display_phone_number: PhoneNumberString; /** * ID for the phone number. A business can respond to a message using this * ID. */ phone_number_id: PhoneNumberID; }; /** * Status object for a message that was sent by the business that is * subscribed to the webhook. */ statuses?: { /** * The ID for the message that the business that is subscribed to the * webhooks sent to a customer. */ id: MessageID; /** * The customer's WhatsApp ID. A business can respond to a customer using * this ID. This ID may not match the customer's phone number, which is * returned by the API as input when sending a message to the customer. */ recipient_id: AccountID; /** * For a status to be read, it must have been delivered. In some scenarios, * such as when a user is in the chat screen and a message arrives, the * message is delivered and read almost simultaneously. In this or other * similar scenarios, the delivered notification will not be sent back, as it * is implied that a message has been delivered if it has been read. The * reason for this behavior is internal optimization. */ status: "delivered" | "read" | "sent" | "failed"; /** * Date for the status message. */ timestamp: number; /** * An object containing pricing information. */ pricing: { /** * Indicates the conversation category. * * @since 4.2.0 */ category: ConversationType; /** * Type of pricing model used by the business. */ pricing_model: "CBP" | (string & NonNullable<unknown>); /** * Indicates if the given message or conversation is billable. Default is * true for all conversations, including those inside your free tier limit, * except those initiated from free entry points. Free entry point * conversation are not billable, false. You will not be charged for free * tier limit conversations, but they are considered billable and will be * reflected on your invoice. * * @deprecated */ billable: boolean; }; /** * Information about the conversation. */ conversation: { /** * Represents the ID of the conversation the given status notification * belongs to. */ id: string; /** * Date when the conversation expires. This field is only present for * messages with a `status` set to `sent`. */ expiration_timestamp?: number; /** * Describes conversation category */ origin: { /** * Indicates conversation category. This can also be referred to as a * conversation entry point. */ type: ConversationType; }; }; /** * Arbitrary string included in sent message. */ biz_opaque_callback_data?: string; }[]; }; }; type WebhookEventNotificationAccountUpdateChanges = { field: "account_update"; value: { event: "PARTNER_APP_INSTALLED" | (string & NonNullable<unknown>); waba_info: { waba_id: AccountID; owner_business_id: BusinessAccountID; partner_app_id: string; }; }; }; type WebhookEventNotification = { /** * The specific webhook a business is subscribed to. The webhook is * whatsapp_business_account. */ object: string; /** * An array of entry objects. */ entry: { /** * The WhatsApp Business Account ID for the business that is subscribed to * the webhook. */ id: BusinessAccountID; /** * An array of change objects. */ changes: (WebhookEventNotificationMessagesChanges | WebhookEventNotificationAccountUpdateChanges)[]; }[]; }; /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare class WebhookError extends Error { } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare class IncorrectMethodWebhookError extends WebhookError { } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare class InvalidHubChallengeWebhookError extends WebhookError { } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare class InvalidHubModeWebhookError extends WebhookError { } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare class InvalidHubSignatureWebhookError extends WebhookError { } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare class InvalidHubVerifyTokenWebhookError extends WebhookError { } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ declare class MissingBodyWebhookError extends WebhookError { } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ interface IncomingRequest { method: string; query: Record<string, string>; body?: string; headers: Record<string, string>; } declare class Webhook { errors: { WebhookError: typeof WebhookError; IncorrectMethodWebhookError: typeof IncorrectMethodWebhookError; InvalidHubChallengeWebhookError: typeof InvalidHubChallengeWebhookError; InvalidHubModeWebhookError: typeof InvalidHubModeWebhookError; InvalidHubSignatureWebhookError: typeof InvalidHubSignatureWebhookError; InvalidHubVerifyTokenWebhookError: typeof InvalidHubVerifyTokenWebhookError; MissingBodyWebhookError: typeof MissingBodyWebhookError; }; /** * Handle a Registration Webhook Request. * The handler for `GET` requests to your webhook endpoint. A registration * request is when WhatsApp sends a GET request to your webhook endpoint to * verify that it is valid. The challenge should be returned if valid. * * **ExpressJS**: * * ```ts * app.get( * "/path/to/webhook", * async (req, res) => { * const reg = await sdk.webhook.register({ * method: request.method, * query: req.query, * body: req.body, * headers: req.headers, * }); * // DIY: Check the reg.verifyToken value * if (reg.verifyToken !== "abcd") { * return res.end(reg.reject()); * } * return res.end(reg.accept()); * } * ); * ``` * * **Fastify**: * * ```ts * fastify.route({ * method: "GET", * url: "/path/to/webhook", * handler: (request, reply) => { * * const reg = await sdk.webhook.register({ * method: request.method, * query: request.query, * body: undefined, * headers: request.headers, * }); * // DIY: Check the reg.verifyToken value * if (reg.verifyToken !== "abcd") { * return reply.send(reg.reject()); * } * return reply.send(reg.accept()); * } * }); * ``` * * @throws {WebhookError} */ register(request: IncomingRequest): Promise<{ verifyToken: string; challenge: string; accept: () => string; reject: () => void; }>; /** * Handle an Event Notification Webhook Request. * The handler for `POST` requests to your webhook endpoint. * * **ExpressJS**: * * ```ts * app.use(express.raw()); // Important <- * app.post( * "/path/to/webhook", * async (req, res) => { * const event = sdk.webhook.eventNotification({ * method: request.method, * query: req.query, * body: req.body.toString(), * headers: req.headers, * }); * // DIY: Load the Meta App Secret * event.verifySignature("abcd-app-secret"); * // Non-200 status codes will be retried * // You may want to use the dreaded "successful error" * if (someFailedCondition) { * res.status(400); * return res.end(); * } * return res.end(event.accept()); * } * ); * ``` * * **Fastify**: * * ```ts * // See: https://github.com/fastify/fastify/issues/707#issuecomment-817224931 * fastify.addContentTypeParser("application/json", { parseAs: "buffer" }, (_req, body, done) => { * done(null, body); * }); * * fastify.route({ * method: "POST", * url: "/path/to/webhook", * handler: (request, reply) => { * // This SDK handles inbound webhook requests from a string for signature verification * assert(Buffer.isBuffer(request.body) || typeof request.body === "string"); * const body = request.body.toString(); * * const event = sdk.webhook.eventNotification({ * method: request.method, * query: request.query, * body, * headers: request.headers, * }); * // DIY: Load the Meta App Secret * event.verifySignature("abcd-app-secret"); * // Non-200 status codes will be retried * // You may want to use the dreaded "successful error" * if (someFailedCondition) { * return reply.code(400).send(); * } * return reply.send(event.accept()); * } * }); * ``` */ eventNotification(request: IncomingRequest): Promise<{ eventNotification: WebhookEventNotification; signature: { sha1: { value: string; getCalculatedSignature: (appSecret: string) => string; check: (appSecret: string) => boolean; }; sha256: { value: string; getCalculatedSignature: (appSecret: string) => string; check: (appSecret: string) => boolean; }; }; checkSignature: (appSecret: string) => boolean; verifySignature(appSecret: string): void; accept: () => void; }>; } /** * WhatsApp NodeJS SDK. * * @author Great Detail Ltd <info@greatdetail.com> * @author Dom Webber <dom.webber@hotmail.com> * @see https://greatdetail.com */ interface Options { prefixUrl?: string; graphVersion?: `v${string}` | (string & NonNullable<unknown>); request?: Omit<Options$1,