The WhatsApp integration connects Cognest to the WhatsApp Business Platform via the Cloud API. It supports sending text, template, and media messages, receiving inbound messages through webhooks, and tracking delivery status updates. Messages are routed through Meta's infrastructure and comply with WhatsApp's 24-hour conversation window policy.
cognest tunnel during development)Test Numbers
Meta provides a test phone number and up to 5 recipient numbers during development. You can send messages to these numbers without incurring charges. Move to a production phone number when you are ready to go live.
Add the WhatsApp integration to your cognest.config.yaml:
integrations:
whatsapp:
enabled: true
credentials:
phone_number_id: ${WHATSAPP_PHONE_ID}
access_token: ${WHATSAPP_ACCESS_TOKEN}
webhook_verify_token: ${WEBHOOK_VERIFY_TOKEN}
settings:
api_version: "v19.0"
webhook_path: "/webhooks/whatsapp"
message_timeout: 30000
retry_attempts: 3
read_receipts: trueStore your credentials in environment variables. Never commit secrets to version control:
# .env
WHATSAPP_PHONE_ID=102938475610293
WHATSAPP_ACCESS_TOKEN=EAABsbCS1iZAg...
WEBHOOK_VERIFY_TOKEN=your-custom-verify-stringIf you prefer programmatic setup over the config file, register the integration directly with the SDK:
import { Cognest } from '@cognest/sdk'
import { WhatsApp } from '@cognest/integrations/whatsapp'
const cognest = new Cognest({ configPath: false })
cognest.use(WhatsApp, {
phoneNumberId: process.env.WHATSAPP_PHONE_ID!,
accessToken: process.env.WHATSAPP_ACCESS_TOKEN!,
webhookVerifyToken: process.env.WEBHOOK_VERIFY_TOKEN!,
settings: {
apiVersion: 'v19.0',
webhookPath: '/webhooks/whatsapp',
messageTimeout: 30_000,
retryAttempts: 3,
readReceipts: true,
},
})
await cognest.start()Access WhatsApp methods through cognest.integration('whatsapp'):
| Method | Parameters | Description |
|---|---|---|
| `send(to, body, options?)` | `to`: phone number (E.164), `body`: message text | Send a plain text message to a recipient |
| `sendTemplate(to, templateName, components?)` | `to`: phone number, `templateName`: approved template ID | Send a pre-approved message template with optional dynamic components |
| `sendMedia(to, media, options?)` | `to`: phone number, `media`: `{ type, url | id, caption? }` | Send an image, video, document, or audio file |
| `markAsRead(messageId)` | `messageId`: the WhatsApp message ID | Mark a received message as read (sends blue checkmarks) |
| `getProfile(phoneNumber)` | `phoneNumber`: E.164 formatted number | Retrieve the contact's WhatsApp profile name and status |
Subscribe to events emitted by the WhatsApp integration. All event handlers receive a typed context object:
| Event | Payload | Description |
|---|---|---|
| `message` | `{ from, body, messageId, timestamp, context? }` | Incoming text message from a user |
| `message:image` | `{ from, imageId, mimeType, caption?, sha256 }` | Incoming image attachment |
| `message:location` | `{ from, latitude, longitude, name?, address? }` | Shared location from a user |
| `message:reaction` | `{ from, messageId, emoji }` | Reaction added to a message |
| `status:delivered` | `{ messageId, recipientId, timestamp }` | Message was delivered to the recipient's device |
| `status:read` | `{ messageId, recipientId, timestamp }` | Message was read by the recipient |
A complete example that listens for incoming messages, processes them with an AI engine, and replies:
import { Cognest } from '@cognest/sdk'
import { WhatsApp } from '@cognest/integrations/whatsapp'
const cognest = new Cognest()
const wa = cognest.integration<WhatsApp>('whatsapp')
// Handle incoming text messages
cognest.on('whatsapp:message', async (ctx) => {
const { from, body, messageId } = ctx.payload
// Mark as read immediately
await wa.markAsRead(messageId)
// Skip if message is empty or a status broadcast
if (!body?.trim() || from === 'status@broadcast') return
// Process with AI engine
const response = await cognest.engine.chat({
conversationId: `wa:${from}`,
message: body,
metadata: {
channel: 'whatsapp',
sender: from,
},
})
// Reply to the user
await wa.send(from, response.text, {
previewUrl: true,
})
})
// Handle image messages
cognest.on('whatsapp:message:image', async (ctx) => {
const { from, imageId, caption } = ctx.payload
await wa.send(from, 'Thanks for the image! Let me take a look.')
// Download and process the image
const imageBuffer = await wa.downloadMedia(imageId)
const analysis = await cognest.engine.vision({
image: imageBuffer,
prompt: caption || 'Describe this image.',
})
await wa.send(from, analysis.text)
})
// Track delivery status
cognest.on('whatsapp:status:delivered', async (ctx) => {
console.log(
`Message ${ctx.payload.messageId} delivered to ${ctx.payload.recipientId}`
)
})
// Send a template message for outbound campaigns
async function sendWelcome(phoneNumber: string, name: string) {
await wa.sendTemplate(phoneNumber, 'welcome_message', [
{
type: 'body',
parameters: [{ type: 'text', text: name }],
},
])
}
await cognest.start()
console.log('WhatsApp bot is running')Common Issues
Webhook verification fails: Ensure your WEBHOOK_VERIFY_TOKEN matches exactly what you configured in the Meta Developer Portal webhook settings. Messages not sending: Check that the recipient has opted in and the 24-hour conversation window is open. For conversations outside the window, use sendTemplate() with an approved template. 403 Forbidden errors: Your access token may have expired. Generate a new permanent token from the System Users section in Meta Business Settings. Media download fails: Media URLs expire after a short period. Download media immediately when the message:image event fires rather than deferring it.