Documentation

WHAM Documentation

Learn how to build WhatsApp Business applications with the wham library. Type-safe, simple, powerful.

Getting Started

Get up and running with wham in under 5 minutes

1. Install the package

bash
npm install @manano-ai/wham

Note: This is a private repository. Join the waitlist to get access.

2. Initialize the client

typescript
import { WhatsApp } from '@manano-ai/wham';
const wa = new WhatsApp({
token: process.env.WHATSAPP_TOKEN!,
phoneNumberId: process.env.PHONE_NUMBER_ID!,
businessAccountId: process.env.BUSINESS_ACCOUNT_ID, // optional
});

3. Send your first message

typescript
// Send a text message
await wa.sendMessage('+1234567890', 'Hello from WHAM!');
// Send a message with buttons
await wa.sendButtons(
'+1234567890',
'How can I help you today?',
[
{ id: 'support', title: 'Get Support' },
{ id: 'sales', title: 'Talk to Sales' },
]
);

Environment Variables

You'll need these credentials from the Meta Developer Portal:

  • WHATSAPP_TOKEN- Your WhatsApp Cloud API access token
  • PHONE_NUMBER_ID- Your WhatsApp phone number ID
  • BUSINESS_ACCOUNT_ID- Your WhatsApp Business Account ID (optional)

What is WHAM?

A type-safe TypeScript client for the WhatsApp Cloud API

wham is a modern TypeScript library that wraps the WhatsApp Cloud API in a clean, type-safe interface. It handles all the complexity of the Meta Graph API so you can focus on building great WhatsApp experiences.

Type-safe API

Full TypeScript support with autocomplete and compile-time error checking

WhatsApp Flows

Build multi-screen forms and experiences with the Flow builder

Template Builder

Create and manage message templates with a fluent API

Webhook Handler

Type-safe event handling with filters and middleware

How it works

wham acts as a bridge between your application and the WhatsApp Cloud API. When you call methods like sendMessage(), wham constructs the proper API request, handles authentication, and returns typed responses.

Your App
wham
WhatsApp API
Users

CLI Reference

Manage WhatsApp resources from the command line

The wham CLI helps you manage WhatsApp Business API resources declaratively. Define templates and flows in code, then sync them with Meta.

Commands

scaffoldDiscover existing resources and generate declarations
importLink declared resources to existing remote resources by name
planShow what changes would be applied to reach the declared state
applyPlan and apply changes to sync declared resources with WhatsApp
refreshCheck remote state for drift, status changes, and deleted resources
previewStart a local preview server for flows and templates
taintMark a resource for forced recreation on the next apply
untaintClear a previously set taint
cleanupDrain old entries from state.deprecated[] (best-effort delete)
registerRegister a phone number with the WhatsApp Business API
deregisterDeregister a phone number from the WhatsApp Business API

Common Options

bash
-c, --config <path> Config file with credentials (default: wham.config.ts)
-r, --resources <path> Resource declarations module (default: wham/resources.ts)
-s, --state <path> State file path (default: .wham-state.json)

Usage Examples

Scaffold existing resources

bash
# Discover all resources
wham scaffold
# Scaffold specific templates and flows
wham scaffold --template hello_world --flow signup_flow
# Custom config and output paths
wham scaffold -c prod.config.ts -r src/wa/resources.ts

Plan and apply changes

bash
# Preview changes
wham plan
# Save plan to file
wham plan --save plan.json
# Apply changes (interactive)
wham apply
# Apply saved plan (CI/CD)
wham apply --plan plan.json --auto-approve

Resource management

bash
# Force recreate a resource
wham taint flow:signup --reason "endpoint health stuck"
# Clear taint
wham untaint flow:signup
# Cleanup old deprecated entries
wham cleanup --older-than 90d --auto-approve
# Preview server for flows
wham preview --port 8888

Phone number registration

bash
# Register phone number
wham register --pin 123456
# Deregister phone number
wham deregister --pin 123456

Config File Format

typescript
// wham.config.ts
export default {
token: process.env.WA_TOKEN,
phoneNumberId: '...',
businessAccountId: '...',
};

Command Options Reference

scaffold

--dry-run, --template <name|id>, --flow <name|id>

plan

--show-orphans, --save <path>

apply

--show-orphans, -p/--plan <path>, --auto-approve, --wait-for-approval, --parallel

preview

--port <port>, --mock-data <path>

refresh

--update-state

taint / untaint

<kind:key>, --reason <text>

cleanup

--older-than <duration>, --target <kind:key>, --auto-approve, --dry-run

register / deregister

--pin <6-digit PIN> (required)

Messaging API

Send text, media, buttons, lists, and more

Text Messages

Send simple text messages to any WhatsApp number.

typescript
// Simple text message
await wa.sendMessage('+1234567890', 'Hello! How can I help you?');
// The method returns the message ID
const messageId = await wa.sendMessage('+1234567890', 'Your order has shipped!');
console.log('Sent message:', messageId);

Images

Send images by URL or media ID with optional captions.

typescript
// Send image by URL
await wa.sendImage('+1234567890', {
link: 'https://example.com/product.jpg',
caption: 'Check out our new product!',
});
// Send image by media ID (after uploading)
await wa.sendImage('+1234567890', { id: 'MEDIA_ID_HERE' });

Documents

Send PDFs, spreadsheets, and other documents.

typescript
await wa.sendDocument('+1234567890', {
link: 'https://example.com/invoice.pdf',
caption: 'Here is your invoice',
filename: 'invoice-2024.pdf',
});

Location

Share locations with coordinates, name, and address.

typescript
await wa.sendLocation('+1234567890', 37.7749, -122.4194, {
name: 'Our Office',
address: '123 Main Street, San Francisco, CA',
});

Interactive Buttons

Send up to 3 quick reply buttons for user selection.

typescript
await wa.sendButtons(
'+1234567890',
'How would you like to pay?',
[
{ id: 'pay_card', title: 'Credit Card' },
{ id: 'pay_cash', title: 'Cash on Delivery' },
{ id: 'pay_transfer', title: 'Bank Transfer' },
],
{
header: 'Payment Options',
footer: 'Choose your preferred method',
}
);

Interactive Lists

Send a list with up to 10 items organized in sections.

typescript
await wa.sendList(
'+1234567890',
'Browse our menu and pick your favorites.',
[
{
title: 'Pizzas',
rows: [
{ id: 'margherita', title: 'Margherita', description: 'Classic tomato & mozzarella' },
{ id: 'pepperoni', title: 'Pepperoni', description: 'Spicy pepperoni & cheese' },
],
},
{
title: 'Drinks',
rows: [
{ id: 'cola', title: 'Cola', description: 'Refreshing cola drink' },
{ id: 'water', title: 'Sparkling Water' },
],
},
],
{
buttonText: 'View Menu',
header: 'Our Menu',
footer: 'Tap an item to order',
}
);

Templates

Pre-approved message formats for notifications and marketing

Note: Templates must be approved by Meta before they can be used. They're required for messaging users outside the 24-hour customer service window.

Sending a Template

typescript
import { BodyText, HeaderImage } from '@manano-ai/wham';
// Simple template with no parameters
await wa.sendTemplate('+1234567890', 'hello_world', { code: 'en_US' });
// Template with parameters
const body = new BodyText(
'Hello {{1}}, your order {{2}} has shipped!',
'Alice', // {{1}} example
'ORD-1234' // {{2}} example
);
await wa.sendTemplate(
'+1234567890',
'order_shipped',
{ code: 'en_US' },
[body.toParams('Alice', 'ORD-1234')]
);

Creating a Template

typescript
import {
Template,
HeaderText,
BodyText,
FooterText,
QuickReplyButton
} from '@manano-ai/wham';
const tmpl = new Template('order_update', 'en_US')
.header(new HeaderText('Order {{1}}', 'ORD-5678'))
.body(new BodyText('Hi {{1}}, your order is {{2}}.', 'Alice', 'on its way'))
.footer(new FooterText('Reply STOP to opt out'))
.buttons(
new QuickReplyButton('Track Order'),
new QuickReplyButton('Get Help')
);
// Submit for approval
await wa.createTemplate('BUSINESS_ACCOUNT_ID', {
name: 'order_update',
language: 'en_US',
category: 'UTILITY',
components: tmpl.toComponents(),
});

Template Components

Headers

  • HeaderText - Text with variables
  • HeaderImage - Image header
  • HeaderVideo - Video header
  • HeaderDocument - Document header

Buttons

  • QuickReplyButton - Quick reply
  • URLButton - Open URL
  • PhoneNumberButton - Call
  • FlowButton - Launch Flow

WhatsApp Flows

Build multi-screen forms and interactive experiences

Pro tip: Flows are perfect for booking forms, surveys, registrations, and any multi-step data collection.

Creating a Flow

typescript
import {
FlowJSON,
FlowJSONVersion,
Screen,
Layout,
TextHeading,
TextInput,
RadioButtonsGroup,
Form,
FlowFooter,
NavigateAction,
CompleteAction,
} from '@manano-ai/wham';
// Create the flow in Meta
const flowId = await wa.createFlow(BUSINESS_ID, {
name: 'Appointment Booking',
categories: ['APPOINTMENT_BOOKING'],
});
// Build the flow screens
const flow = new FlowJSON({
version: FlowJSONVersion.LATEST,
screens: [
new Screen({
id: 'SELECT_SERVICE',
title: 'Book Appointment',
layout: new Layout({
children: [
new TextHeading({ text: 'Choose a Service' }),
new Form({
name: 'service_form',
children: [
new RadioButtonsGroup({
name: 'service',
label: 'Service Type',
dataSource: [
{ id: 'haircut', title: 'Haircut - $30' },
{ id: 'color', title: 'Hair Color - $80' },
{ id: 'style', title: 'Styling - $50' },
],
required: true,
}),
],
onSubmitAction: new NavigateAction({ next: 'CONTACT_INFO' }),
}),
new FlowFooter({
label: 'Continue',
onClickAction: { name: 'submit', payload: {} }
}),
],
}),
}),
new Screen({
id: 'CONTACT_INFO',
title: 'Your Details',
terminal: true,
layout: new Layout({
children: [
new TextHeading({ text: 'Contact Information' }),
new Form({
name: 'contact_form',
children: [
new TextInput({
name: 'name',
label: 'Full Name',
required: true
}),
new TextInput({
name: 'phone',
label: 'Phone Number',
inputType: 'phone',
required: true
}),
],
onSubmitAction: new CompleteAction(),
}),
new FlowFooter({
label: 'Book Now',
onClickAction: { name: 'submit', payload: {} }
}),
],
}),
}),
],
});
// Upload and publish
await wa.updateFlowJson(flowId, flow.toJSON());
await wa.publishFlow(flowId);

Sending a Flow

typescript
await wa.sendFlow(
'+1234567890',
'Book your appointment online.',
flowId,
'unique-session-123', // Token to track this session
{
flowCta: 'Book Now',
flowAction: 'navigate',
screenId: 'SELECT_SERVICE',
flowData: {
preselected_service: 'haircut'
},
}
);

Flow Components

Text

  • TextHeading
  • TextSubheading
  • TextBody
  • TextCaption
  • RichText

Inputs

  • TextInput
  • TextArea
  • DatePicker
  • Dropdown
  • RadioButtonsGroup
  • CheckboxGroup

Actions

  • NavigateAction
  • CompleteAction
  • DataExchangeAction
  • OpenURLAction

Webhooks & Events

Handle incoming messages and events with type safety

Setting up the Webhook Handler

typescript
import { WebhookHandler, filters } from '@manano-ai/wham';
const handler = new WebhookHandler();
// Handle all incoming messages
handler.onMessage((msg) => {
console.log(`New message from ${msg.from}: ${msg.type}`);
});
// Handle only text messages
handler.onMessage(filters.text, async (msg) => {
if (msg.type === 'text') {
const text = msg.text.body.toLowerCase();
if (text.includes('hello')) {
await wa.sendMessage(msg.from, 'Hi there! How can I help?');
}
}
});
// Handle button clicks
handler.onCallbackButton(async (event) => {
console.log(`User clicked: ${event.title}`);
if (event.data === 'get_support') {
await wa.sendMessage(event.from, 'A support agent will be with you shortly.');
}
});
// Handle list selections
handler.onCallbackSelection(async (event) => {
console.log(`User selected: ${event.title}`);
});
// Handle flow completions
handler.onFlowCompletion(async (event) => {
console.log(`Flow completed: ${event.flow_token}`);
console.log('Response data:', event.response);
});

Express Middleware

typescript
import express from 'express';
import { whatsappWebhook } from '@manano-ai/wham/express';
const app = express();
app.use('/webhook', whatsappWebhook({
handler,
verifyToken: process.env.VERIFY_TOKEN!,
appSecret: process.env.APP_SECRET, // For signature validation
}));
app.listen(3000);

Message Filters

Type Filters

  • filters.text - Text messages
  • filters.image - Images
  • filters.document - Documents
  • filters.location - Locations
  • filters.audio - Audio/voice
  • filters.media - Any media

Content Filters

  • filters.contains('word')
  • filters.startsWith('hi')
  • filters.matches(/regex/)
  • filters.command('help')
  • filter1.and(filter2)
  • filter1.or(filter2)

Ready to build?

Try the AI builder to generate your WhatsApp bot code, or get started with the library directly.