Skip to content
Playground

Message Utilities

The SDK provides utility functions to simplify working with incoming webhook messages. These include type guards for narrowing message types, a universal text extractor, and structured data extractors for common message fields.

Import

import {
// Text extraction
extractMessageText,
// Type guards
isTextMessage,
isImageMessage,
isVideoMessage,
isAudioMessage,
isDocumentMessage,
isStickerMessage,
isInteractiveMessage,
isButtonMessage,
isLocationMessage,
isContactsMessage,
isReactionMessage,
isOrderMessage,
isSystemMessage,
// Interactive sub-type guards
isButtonReply,
isListReply,
isNfmReply,
// Structured extractors
getMediaInfo,
getInteractiveReply,
getLocationInfo,
getContactsInfo,
getReactionInfo,
getOrderInfo,
} from 'meta-cloud-api';

Text Extraction

extractMessageText returns a human-readable display string from any incoming message. Useful for logging, notifications, chat previews, and summaries.

processor.onMessage(MessageTypesEnum['*'], async (whatsapp, { message }) => {
const text = extractMessageText(message);
console.log(`${message.from}: ${text}`);
});

Output by message type:

Message TypeOutput
Textmessage.text.body
ImageCaption or [Image]
VideoCaption or [Video]
Audio[Voice Message] or [Audio]
DocumentCaption, filename, or [Document]
Sticker[Animated Sticker] or [Sticker]
InteractiveButton/list title or [Form Reply]
Buttonmessage.button.text
LocationName, address, or coordinates
Contacts👤 Name or 👤 N contacts
ReactionEmoji or [Reaction removed]
Order🛒 Order: N items
Systemmessage.system.body

Type Guards

Type guards narrow the WhatsAppMessage union type to a specific message type, giving you full type safety:

import { isTextMessage, isImageMessage } from 'meta-cloud-api';
function handleMessage(message: WhatsAppMessage) {
if (isTextMessage(message)) {
// TypeScript knows: message is TextMessage
console.log(message.text.body);
}
if (isImageMessage(message)) {
// TypeScript knows: message is ImageMessage
console.log(message.image.id, message.image.caption);
}
}

Interactive Sub-Type Guards

For interactive messages, additional guards narrow to the specific reply type:

import { isButtonReply, isListReply, isNfmReply } from 'meta-cloud-api';
processor.onInteractive(async (whatsapp, { message }) => {
if (isButtonReply(message)) {
console.log('Button:', message.interactive.button_reply.id);
}
if (isListReply(message)) {
console.log('List:', message.interactive.list_reply.id);
}
if (isNfmReply(message)) {
const data = JSON.parse(message.interactive.nfm_reply.response_json);
console.log('Flow data:', data);
}
});

Available Type Guards

FunctionNarrows to
isTextMessageTextMessage
isImageMessageImageMessage
isVideoMessageVideoMessage
isAudioMessageAudioMessage
isDocumentMessageDocumentMessage
isStickerMessageStickerMessage
isInteractiveMessageInteractiveMessage
isButtonMessageButtonMessage
isLocationMessageLocationMessage
isContactsMessageContactsMessage
isReactionMessageReactionMessage
isOrderMessageOrderMessage
isSystemMessageSystemMessage
isButtonReplyInteractiveButtonReplyMessage
isListReplyInteractiveListReplyMessage
isNfmReplyInteractiveNfmReplyMessage

Structured Data Extractors

These functions extract normalized data from specific message types, returning null if the message doesn’t match.

getMediaInfo

Extract media metadata from image, video, audio, document, or sticker messages:

import { getMediaInfo } from 'meta-cloud-api';
import type { MediaInfo } from 'meta-cloud-api';
processor.onMessage(MessageTypesEnum['*'], async (whatsapp, { message }) => {
const media = getMediaInfo(message);
if (media) {
console.log(`Media ID: ${media.id}`);
console.log(`MIME: ${media.mimeType}`);
console.log(`Caption: ${media.caption}`);
console.log(`Filename: ${media.filename}`); // documents only
console.log(`Animated: ${media.animated}`); // stickers only
console.log(`Voice: ${media.voice}`); // audio only
// Download the media
const url = await whatsapp.media.retrieveMediaUrl({ mediaId: media.id });
}
});

getInteractiveReply

Extract the user’s selection from button, list, or flow replies:

import { getInteractiveReply } from 'meta-cloud-api';
processor.onInteractive(async (whatsapp, { message }) => {
const reply = getInteractiveReply(message);
if (reply) {
console.log(`Type: ${reply.type}`); // 'button_reply' | 'list_reply' | 'nfm_reply'
console.log(`ID: ${reply.id}`);
console.log(`Title: ${reply.title}`);
console.log(`Description: ${reply.description}`); // list_reply only
console.log(`JSON: ${reply.responseJson}`); // nfm_reply only
}
});

getLocationInfo

import { getLocationInfo } from 'meta-cloud-api';
processor.onLocation(async (whatsapp, { message }) => {
const loc = getLocationInfo(message);
if (loc) {
console.log(`${loc.name} - ${loc.address}`);
console.log(`Coordinates: ${loc.latitude}, ${loc.longitude}`);
}
});

getContactsInfo

import { getContactsInfo } from 'meta-cloud-api';
processor.onContacts(async (whatsapp, { message }) => {
const contacts = getContactsInfo(message);
for (const contact of contacts) {
console.log(`Name: ${contact.formattedName}`);
console.log(`Phones: ${contact.phones.join(', ')}`);
console.log(`Emails: ${contact.emails.join(', ')}`);
}
});

getReactionInfo

import { getReactionInfo } from 'meta-cloud-api';
processor.onReaction(async (whatsapp, { message }) => {
const reaction = getReactionInfo(message);
if (reaction) {
if (reaction.emoji) {
console.log(`Reacted with ${reaction.emoji} to ${reaction.messageId}`);
} else {
console.log(`Removed reaction from ${reaction.messageId}`);
}
}
});

getOrderInfo

import { getOrderInfo } from 'meta-cloud-api';
processor.onOrder(async (whatsapp, { message }) => {
const order = getOrderInfo(message);
if (order) {
console.log(`Catalog: ${order.catalogId}`);
for (const item of order.items) {
console.log(` ${item.productId}: ${item.quantity}x ${item.currency} ${item.price}`);
}
}
});

Practical Example

Combining utilities for a unified message handler:

import {
extractMessageText,
getMediaInfo,
getInteractiveReply,
isTextMessage,
} from 'meta-cloud-api';
processor.onMessage(MessageTypesEnum['*'], async (whatsapp, processed) => {
const { message } = processed;
const preview = extractMessageText(message);
// Log all messages
await saveToDatabase({
from: message.from,
type: message.type,
preview,
timestamp: message.timestamp,
});
// Download media if present
const media = getMediaInfo(message);
if (media) {
const url = await whatsapp.media.retrieveMediaUrl({ mediaId: media.id });
await downloadAndStore(url.url, media.mimeType);
}
// Handle interactive replies
const reply = getInteractiveReply(message);
if (reply) {
await handleUserSelection(message.from, reply.id, reply.type);
return;
}
// Handle text commands
if (isTextMessage(message) && message.text.body.startsWith('/')) {
await handleCommand(whatsapp, message);
return;
}
// Default echo
await whatsapp.messages.text({
to: message.from,
body: `Received: ${preview}`,
});
});