Skip to main content

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' });

Contacts

// 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.
I