Conversations
import { buildKapsoFields } from '@kapso/whatsapp-cloud-api';
// List
const conversations = await client.conversations.list({
phoneNumberId: '123',
status: 'active',
lastActiveSince: '2025-01-01T00:00:00Z',
limit: 20,
fields: buildKapsoFields(['contact_name'])
});
// Get one (includes phoneNumberId when using Kapso proxy)
const conv = await client.conversations.get({ conversationId: 'conv-123' });
// Update status
await client.conversations.updateStatus({ conversationId: 'conv-123', status: 'ended' });
// List (Kapso proxy)
const contacts = await client.contacts.list({
phoneNumberId: '123',
customerId: '00000000-0000-0000-0000-000000000000',
limit: 50,
fields: buildKapsoFields(['contact_name'])
});
// Get one
const contact = await client.contacts.get({ phoneNumberId: '123', waId: '56911112222' });
// Update
await client.contacts.update({
phoneNumberId: '123',
waId: '56911112222',
profileName: 'Alice',
metadata: { tags: ['vip'] }
});
Calls (Calling API)
// Initiate (business-initiated)
await client.calls.connect({
phoneNumberId: '123',
to: '14085551234',
session: { sdpType: 'offer', sdp: 'v=0...' },
bizOpaqueCallbackData: 'tracking'
});
// Pre-accept / Accept / Reject / Terminate
await client.calls.preAccept({ phoneNumberId: '123', callId: 'wacid...', session: { sdpType: 'answer', sdp: 'v=0' } });
await client.calls.accept({ phoneNumberId: '123', callId: 'wacid...', session: { sdpType: 'answer', sdp: 'v=0' } });
await client.calls.reject({ phoneNumberId: '123', callId: 'wacid...' });
await client.calls.terminate({ phoneNumberId: '123', callId: 'wacid...' });
// Permission check
const perm = await client.calls.permissions.get({ phoneNumberId: '123', userWaId: '15551234567' });
// Kapso call logs
const callPage = await client.calls.list({
phoneNumberId: '123',
direction: 'INBOUND',
limit: 20,
fields: buildKapsoFields()
});
Phone number settings & profile
// Settings
const settings = await client.phoneNumbers.settings.get({ phoneNumberId: '123' });
await client.phoneNumbers.settings.update({ phoneNumberId: '123', fallbackLanguage: 'en_US' });
// Business profile
const profile = await client.phoneNumbers.businessProfile.get({ phoneNumberId: '123' });
await client.phoneNumbers.businessProfile.update({ phoneNumberId: '123', about: 'My business' });
Kapso field helpers
import {
KAPSO_MESSAGE_FIELDS,
buildKapsoFields,
buildKapsoMessageFields
} from '@kapso/whatsapp-cloud-api';
// Request every Kapso message extra (same as fields: 'kapso(default)')
const selector = buildKapsoFields();
// Opt into a subset
const lightweight = buildKapsoMessageFields('flow_response', 'flow_token');
const history = await client.messages.query({
phoneNumberId: '123',
limit: 20,
fields: lightweight
});
// Backend also accepts the raw strings 'kapso(default)' or 'kapso(*)'
Webhooks
import express from 'express';
import { normalizeWebhook, verifySignature } from '@kapso/whatsapp-cloud-api/server';
const app = express();
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
if (!verifySignature({
appSecret: process.env.META_APP_SECRET!,
rawBody: req.body,
signatureHeader: req.headers['x-hub-signature-256'] as string
})) {
return res.status(401).end();
}
const payload = JSON.parse(req.body.toString('utf8'));
const events = normalizeWebhook(payload);
events.messages.forEach((message) => {
console.log(message.type, message.kapso?.direction);
});
events.raw.accountAlerts?.forEach((alert) => {
console.log('Account alert', alert.alertInfo?.alertType);
});
res.sendStatus(200);
});
normalizeWebhook()
unwraps Meta’s Graph payload, converts it to the same structure returned by messages.query
, and sets kapso.direction
("inbound"
/"outbound"
). SMB echoes are tagged with kapso.source = "smb_message_echo"
, and every other webhook field (for example accountAlerts
, templateCategoryUpdate
) is available under events.raw.<fieldName>
.
See the full list of Kapso fields in Kapso Extensions.
verifySignature
depends on Node’s crypto
module and is intended for server runtimes. If your bundler shows a warning about node:crypto
being external, that’s expected — mark it as external or set the bundler platform to node
.