WhatsApp API
Send WhatsApp broadcasts, manage templates, contacts, and conversations with Late API
Quick Reference
| Property | Value |
|---|---|
| Message types | Text, Template, Image, Video, Document, Audio |
| Template required | Yes (outside 24h conversation window) |
| Image formats | JPEG, PNG |
| Image max size | 5 MB |
| Video formats | MP4, 3GPP |
| Video max size | 16 MB |
| Document formats | PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT |
| Document max size | 100 MB |
| Audio formats | MP3, OGG, AMR, AAC |
| Audio max size | 16 MB |
| Scheduling | No (use broadcasts with scheduling) |
| Inbox (DMs) | Yes (add-on) |
| Inbox (Comments) | No |
| Analytics | No |
Before You Start
WhatsApp requires a WhatsApp Business Account (WABA) connected via Meta's Embedded Signup. This is different from a regular WhatsApp account.
Requirements:
- A Meta Business account (business.facebook.com)
- WhatsApp Business Account created through Meta's Embedded Signup flow
- At least one phone number registered with the WABA
- Approved message templates for initiating conversations (Meta reviews these)
- 24-hour rule: You can only send free-form messages within 24 hours of the customer's last message. Outside that window, you must use an approved template.
Quick Start
Send a template message to multiple recipients via bulk send:
import Late from '@getlatedev/node';
const late = new Late();
const { data } = await late.whatsapp.sendWhatsAppBulk({
body: {
accountId: 'YOUR_WHATSAPP_ACCOUNT_ID',
recipients: [
{ phone: '+1234567890', variables: { '1': 'John' } },
{ phone: '+0987654321', variables: { '1': 'Jane' } }
],
template: {
name: 'hello_world',
language: 'en'
}
}
});
console.log(`Sent: ${data.summary.sent}, Failed: ${data.summary.failed}`);from late import Late
client = Late()
response = client.whatsapp.send_whats_app_bulk(
account_id='YOUR_WHATSAPP_ACCOUNT_ID',
recipients=[
{'phone': '+1234567890', 'variables': {'1': 'John'}},
{'phone': '+0987654321', 'variables': {'1': 'Jane'}}
],
template={
'name': 'hello_world',
'language': 'en'
}
)
print(f"Sent: {response.summary.sent}, Failed: {response.summary.failed}")curl -X POST https://getlate.dev/api/v1/whatsapp/bulk \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "YOUR_WHATSAPP_ACCOUNT_ID",
"recipients": [
{"phone": "+1234567890", "variables": {"1": "John"}},
{"phone": "+0987654321", "variables": {"1": "Jane"}}
],
"template": {
"name": "hello_world",
"language": "en"
}
}'Broadcasts
Send template messages to many recipients at once. Broadcasts support per-recipient variables, scheduling, and delivery tracking.
Create a Broadcast
const { data } = await late.whatsapp.createWhatsAppBroadcast({
body: {
accountId: 'YOUR_WHATSAPP_ACCOUNT_ID',
name: 'January Newsletter',
template: {
name: 'monthly_update',
language: 'en',
components: [{
type: 'body',
parameters: [{ type: 'text', text: '{{1}}' }]
}]
},
recipients: [
{ phone: '+1234567890', name: 'John', variables: { '1': 'John' } },
{ phone: '+0987654321', name: 'Jane', variables: { '1': 'Jane' } }
]
}
});
console.log('Broadcast created:', data.broadcast.id);response = client.whatsapp.create_whats_app_broadcast(
account_id='YOUR_WHATSAPP_ACCOUNT_ID',
name='January Newsletter',
template={
'name': 'monthly_update',
'language': 'en',
'components': [{
'type': 'body',
'parameters': [{'type': 'text', 'text': '{{1}}'}]
}]
},
recipients=[
{'phone': '+1234567890', 'name': 'John', 'variables': {'1': 'John'}},
{'phone': '+0987654321', 'name': 'Jane', 'variables': {'1': 'Jane'}}
]
)
print(f"Broadcast created: {response.broadcast.id}")curl -X POST https://getlate.dev/api/v1/whatsapp/broadcasts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "YOUR_WHATSAPP_ACCOUNT_ID",
"name": "January Newsletter",
"template": {
"name": "monthly_update",
"language": "en",
"components": [{
"type": "body",
"parameters": [{"type": "text", "text": "{{1}}"}]
}]
},
"recipients": [
{"phone": "+1234567890", "name": "John", "variables": {"1": "John"}},
{"phone": "+0987654321", "name": "Jane", "variables": {"1": "Jane"}}
]
}'Send a Broadcast
const { data } = await late.whatsapp.sendWhatsAppBroadcast({
path: { broadcastId: 'BROADCAST_ID' }
});
console.log(`Sent: ${data.sent}, Failed: ${data.failed}`);response = client.whatsapp.send_whats_app_broadcast(
broadcast_id='BROADCAST_ID'
)
print(f"Sent: {response.sent}, Failed: {response.failed}")curl -X POST "https://getlate.dev/api/v1/whatsapp/broadcasts/BROADCAST_ID/send" \
-H "Authorization: Bearer YOUR_API_KEY"Schedule a Broadcast
await late.whatsapp.scheduleWhatsAppBroadcast({
path: { broadcastId: 'BROADCAST_ID' },
body: { scheduledAt: '2025-02-01T10:00:00.000Z' }
});client.whatsapp.schedule_whats_app_broadcast(
broadcast_id='BROADCAST_ID',
scheduled_at='2025-02-01T10:00:00.000Z'
)curl -X POST "https://getlate.dev/api/v1/whatsapp/broadcasts/BROADCAST_ID/schedule" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scheduledAt": "2025-02-01T10:00:00.000Z"}'Add Recipients to a Broadcast
const { data } = await late.whatsapp.addWhatsAppBroadcastRecipients({
path: { broadcastId: 'BROADCAST_ID' },
body: {
recipients: [
{ phone: '+1555000111', name: 'Alice', variables: { '1': 'Alice' } },
{ phone: '+1555000222', name: 'Bob', variables: { '1': 'Bob' } }
]
}
});
console.log(`Added: ${data.added}, Duplicates: ${data.duplicates}`);response = client.whatsapp.add_whats_app_broadcast_recipients(
broadcast_id='BROADCAST_ID',
recipients=[
{'phone': '+1555000111', 'name': 'Alice', 'variables': {'1': 'Alice'}},
{'phone': '+1555000222', 'name': 'Bob', 'variables': {'1': 'Bob'}}
]
)
print(f"Added: {response.added}, Duplicates: {response.duplicates}")curl -X PATCH "https://getlate.dev/api/v1/whatsapp/broadcasts/BROADCAST_ID/recipients" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipients": [
{"phone": "+1555000111", "name": "Alice", "variables": {"1": "Alice"}},
{"phone": "+1555000222", "name": "Bob", "variables": {"1": "Bob"}}
]
}'Templates
Templates are required for initiating conversations outside the 24-hour messaging window. They must be submitted to Meta for approval before use.
List Templates
const { data } = await late.whatsapp.getWhatsAppTemplates({
query: { accountId: 'YOUR_ACCOUNT_ID' }
});
data.templates.forEach(t => console.log(`${t.name} (${t.status}) - ${t.language}`));response = client.whatsapp.get_whats_app_templates(
account_id='YOUR_ACCOUNT_ID'
)
for t in response.templates:
print(f"{t.name} ({t.status}) - {t.language}")curl "https://getlate.dev/api/v1/whatsapp/templates?accountId=YOUR_ACCOUNT_ID" \
-H "Authorization: Bearer YOUR_API_KEY"Create a Template
const { data } = await late.whatsapp.createWhatsAppTemplate({
body: {
accountId: 'YOUR_ACCOUNT_ID',
name: 'order_confirmation',
category: 'UTILITY',
language: 'en',
components: [{ type: 'BODY', text: 'Hi {{1}}, your order {{2}} has been confirmed!' }]
}
});
console.log(`Template created: ${data.template.name} (${data.template.status})`);response = client.whatsapp.create_whats_app_template(
account_id='YOUR_ACCOUNT_ID',
name='order_confirmation',
category='UTILITY',
language='en',
components=[{'type': 'BODY', 'text': 'Hi {{1}}, your order {{2}} has been confirmed!'}]
)
print(f"Template created: {response.template.name} ({response.template.status})")curl -X POST https://getlate.dev/api/v1/whatsapp/templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "YOUR_ACCOUNT_ID",
"name": "order_confirmation",
"category": "UTILITY",
"language": "en",
"components": [{"type": "BODY", "text": "Hi {{1}}, your order {{2}} has been confirmed!"}]
}'Templates are reviewed by Meta and can take up to 24 hours to be approved. Only approved templates can be used for sending messages.
Contacts
Manage your WhatsApp contact list for broadcasts. Import contacts individually, in bulk, or via CSV.
Create a Contact
const { data } = await late.whatsapp.createWhatsAppContact({
body: {
accountId: 'YOUR_ACCOUNT_ID',
phone: '+1234567890',
name: 'John Doe',
email: 'john@example.com',
tags: ['vip', 'newsletter'],
groups: ['customers']
}
});
console.log('Contact created:', data.contact.id);response = client.whatsapp.create_whats_app_contact(
account_id='YOUR_ACCOUNT_ID',
phone='+1234567890',
name='John Doe',
email='john@example.com',
tags=['vip', 'newsletter'],
groups=['customers']
)
print(f"Contact created: {response.contact.id}")curl -X POST https://getlate.dev/api/v1/whatsapp/contacts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "YOUR_ACCOUNT_ID",
"phone": "+1234567890",
"name": "John Doe",
"email": "john@example.com",
"tags": ["vip", "newsletter"],
"groups": ["customers"]
}'Bulk Import
const { data } = await late.whatsapp.importWhatsAppContacts({
body: {
accountId: 'YOUR_ACCOUNT_ID',
contacts: [
{ phone: '+1234567890', name: 'John Doe', tags: ['vip'] },
{ phone: '+0987654321', name: 'Jane Smith', tags: ['newsletter'] }
],
defaultTags: ['imported'],
skipDuplicates: true
}
});
console.log(`Created: ${data.summary.created}, Skipped: ${data.summary.skipped}`);response = client.whatsapp.import_whats_app_contacts(
account_id='YOUR_ACCOUNT_ID',
contacts=[
{'phone': '+1234567890', 'name': 'John Doe', 'tags': ['vip']},
{'phone': '+0987654321', 'name': 'Jane Smith', 'tags': ['newsletter']}
],
default_tags=['imported'],
skip_duplicates=True
)
print(f"Created: {response.summary.created}, Skipped: {response.summary.skipped}")curl -X POST https://getlate.dev/api/v1/whatsapp/contacts/import \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "YOUR_ACCOUNT_ID",
"contacts": [
{"phone": "+1234567890", "name": "John Doe", "tags": ["vip"]},
{"phone": "+0987654321", "name": "Jane Smith", "tags": ["newsletter"]}
],
"defaultTags": ["imported"],
"skipDuplicates": true
}'Bulk Operations
// Add tags to multiple contacts
const { data } = await late.whatsapp.bulkUpdateWhatsAppContacts({
body: {
action: 'addTags',
contactIds: ['contact_1', 'contact_2', 'contact_3'],
tags: ['promo-march']
}
});
console.log(`Modified: ${data.modified}`);# Add tags to multiple contacts
response = client.whatsapp.bulk_update_whats_app_contacts(
action='addTags',
contact_ids=['contact_1', 'contact_2', 'contact_3'],
tags=['promo-march']
)
print(f"Modified: {response.modified}")curl -X POST https://getlate.dev/api/v1/whatsapp/contacts/bulk \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "addTags",
"contactIds": ["contact_1", "contact_2", "contact_3"],
"tags": ["promo-march"]
}'Available bulk actions: addTags, removeTags, addGroups, removeGroups, optIn, optOut, block, unblock.
Business Profile
// Get business profile
const { data } = await late.whatsapp.getWhatsAppBusinessProfile({
query: { accountId: 'YOUR_ACCOUNT_ID' }
});
console.log(data.businessProfile);
// Update business profile
await late.whatsapp.updateWhatsAppBusinessProfile({
body: {
accountId: 'YOUR_ACCOUNT_ID',
about: 'Your go-to store for widgets',
description: 'We sell the best widgets in town.',
email: 'hello@example.com',
websites: ['https://example.com']
}
});# Get business profile
response = client.whatsapp.get_whats_app_business_profile(
account_id='YOUR_ACCOUNT_ID'
)
print(response.business_profile)
# Update business profile
client.whatsapp.update_whats_app_business_profile(
account_id='YOUR_ACCOUNT_ID',
about='Your go-to store for widgets',
description='We sell the best widgets in town.',
email='hello@example.com',
websites=['https://example.com']
)# Get business profile
curl "https://getlate.dev/api/v1/whatsapp/business-profile?accountId=YOUR_ACCOUNT_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
# Update business profile
curl -X POST https://getlate.dev/api/v1/whatsapp/business-profile \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"accountId": "YOUR_ACCOUNT_ID",
"about": "Your go-to store for widgets",
"description": "We sell the best widgets in town.",
"email": "hello@example.com",
"websites": ["https://example.com"]
}'Phone Numbers
Manage WhatsApp phone numbers: search, purchase, verify, and release.
List Your Numbers
const { data } = await late.whatsappphonenumbers.getWhatsAppPhoneNumbers();
data.numbers.forEach(n => console.log(`${n.phoneNumber} (${n.status})`));response = client.whatsappphonenumbers.get_whats_app_phone_numbers()
for n in response.numbers:
print(f"{n.phone_number} ({n.status})")curl "https://getlate.dev/api/v1/whatsapp/phone-numbers" \
-H "Authorization: Bearer YOUR_API_KEY"Purchase a Number
const { data } = await late.whatsappphonenumbers.purchaseWhatsAppPhoneNumber({
body: { profileId: 'YOUR_PROFILE_ID' }
});
// First number returns a Stripe checkout URL
if (data.checkoutUrl) {
console.log('Complete payment:', data.checkoutUrl);
} else {
console.log('Number provisioned:', data.phoneNumber.phoneNumber);
}response = client.whatsappphonenumbers.purchase_whats_app_phone_number(
profile_id='YOUR_PROFILE_ID'
)
# First number returns a Stripe checkout URL
if hasattr(response, 'checkout_url') and response.checkout_url:
print(f"Complete payment: {response.checkout_url}")
else:
print(f"Number provisioned: {response.phone_number.phone_number}")curl -X POST https://getlate.dev/api/v1/whatsapp/phone-numbers/purchase \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"profileId": "YOUR_PROFILE_ID"}'Verify a Number
After purchasing, verify the number with Meta by requesting an OTP and submitting it:
// Step 1: Request verification code
await late.whatsappphonenumbers.requestWhatsAppVerificationCode({
path: { phoneNumberId: 'PHONE_NUMBER_ID' },
body: { method: 'SMS' }
});
// Step 2: Submit the code
const { data } = await late.whatsappphonenumbers.verifyWhatsAppPhoneNumber({
path: { phoneNumberId: 'PHONE_NUMBER_ID' },
body: { code: '123456' }
});
console.log('Verified until:', data.metaVerificationExpiresAt);# Step 1: Request verification code
client.whatsappphonenumbers.request_whats_app_verification_code(
phone_number_id='PHONE_NUMBER_ID',
method='SMS'
)
# Step 2: Submit the code
response = client.whatsappphonenumbers.verify_whats_app_phone_number(
phone_number_id='PHONE_NUMBER_ID',
code='123456'
)
print(f"Verified until: {response.meta_verification_expires_at}")# Step 1: Request verification code
curl -X POST "https://getlate.dev/api/v1/whatsapp/phone-numbers/PHONE_NUMBER_ID/request-code" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"method": "SMS"}'
# Step 2: Submit the code
curl -X POST "https://getlate.dev/api/v1/whatsapp/phone-numbers/PHONE_NUMBER_ID/verify" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"code": "123456"}'Media Requirements
Images
| Property | Requirement |
|---|---|
| Formats | JPEG, PNG |
| Max file size | 5 MB |
Videos
| Property | Requirement |
|---|---|
| Formats | MP4, 3GPP |
| Max file size | 16 MB |
| Codec | H.264 video, AAC audio |
Documents
| Property | Requirement |
|---|---|
| Formats | PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX, TXT |
| Max file size | 100 MB |
Audio
| Property | Requirement |
|---|---|
| Formats | MP3, OGG (with opus codec), AMR, AAC |
| Max file size | 16 MB |
Connection
Unlike other platforms that use standard OAuth, WhatsApp requires a WhatsApp Business Account (WABA) and uses Meta's infrastructure for authentication. There are two ways to connect:
Option 1: Embedded Signup (Browser Required)
Meta's Embedded Signup opens a popup where the user logs into Facebook, selects or creates a WABA, and picks a phone number. This is the standard flow when your users connect through a browser UI.
- User clicks "Connect WhatsApp" in your app
- Facebook JS SDK opens Embedded Signup popup
- User selects or creates a WhatsApp Business Account
- User picks a phone number
- Late exchanges the code for tokens and creates the connection
const { data } = await late.connect.handleOAuthCallback({
body: {
platform: 'whatsapp',
code: 'FACEBOOK_AUTH_CODE',
profileId: 'YOUR_PROFILE_ID'
}
});
console.log('Connected:', data.account.id);response = client.connect.handle_oauth_callback(
platform='whatsapp',
code='FACEBOOK_AUTH_CODE',
profile_id='YOUR_PROFILE_ID'
)
print(f"Connected: {response.account.id}")curl -X POST https://getlate.dev/api/v1/connect/whatsapp/embedded-signup \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"code": "FACEBOOK_AUTH_CODE",
"profileId": "YOUR_PROFILE_ID"
}'Option 2: Headless Credentials (No Browser)
If you already have your Meta credentials, you can connect entirely via API with no browser popup. This is ideal for server-to-server integrations, CLI tools, or automated provisioning.
Prerequisites: Create a System User in Meta Business Suite, generate a permanent access token with whatsapp_business_management and whatsapp_business_messaging permissions, and get your WABA ID and Phone Number ID from the WhatsApp Manager.
const { data } = await late.post('/v1/connect/whatsapp/credentials', {
profileId: 'YOUR_PROFILE_ID',
accessToken: 'YOUR_META_SYSTEM_USER_TOKEN',
wabaId: 'YOUR_WABA_ID',
phoneNumberId: 'YOUR_PHONE_NUMBER_ID'
});
console.log('Connected:', data.account.accountId);response = client.post('/v1/connect/whatsapp/credentials', json={
'profileId': 'YOUR_PROFILE_ID',
'accessToken': 'YOUR_META_SYSTEM_USER_TOKEN',
'wabaId': 'YOUR_WABA_ID',
'phoneNumberId': 'YOUR_PHONE_NUMBER_ID'
})
print(f"Connected: {response.json()['account']['accountId']}")curl -X POST https://getlate.dev/api/v1/connect/whatsapp/credentials \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profileId": "YOUR_PROFILE_ID",
"accessToken": "YOUR_META_SYSTEM_USER_TOKEN",
"wabaId": "YOUR_WABA_ID",
"phoneNumberId": "YOUR_PHONE_NUMBER_ID"
}'The endpoint validates your credentials against Meta's API, creates the connection, subscribes to webhooks, and registers the phone number on the WhatsApp network. If the phoneNumberId is not found in your WABA, the response includes available phone numbers so you can correct it.
Analytics
WhatsApp does not provide post-level analytics through its API. Message delivery status (sent, delivered, read) is tracked per-recipient in broadcasts.
What You Can't Do
- Send free-form messages outside the 24-hour conversation window (must use templates)
- Send messages to numbers without WhatsApp
- Use personal WhatsApp accounts (must be WhatsApp Business)
- Send more than 250 unique contacts/day on a new account (tier 0 limit, increases with usage)
- Schedule individual messages (use broadcasts with scheduling instead)
- Get per-message analytics (only delivery status)
Common Errors
| Error | Cause | Fix |
|---|---|---|
| "Template not found" (132001) | Template name or language code doesn't match | Verify the exact template name and language code (e.g., "en" not "en_US") |
| "Invalid phone number" (131021) | Recipient number format is incorrect or doesn't have WhatsApp | Use E.164 format with country code (e.g., +1234567890) |
| "Re-engagement required" (131026) | 24-hour conversation window expired | Send an approved template message to re-initiate |
| "Rate limit hit" (131047) | Too many messages sent too quickly | Reduce sending frequency, wait for rate limit to reset |
| "Media download failed" (131052) | WhatsApp can't fetch media from the URL | Ensure URL is publicly accessible with no auth required |
| "Not in allowed list" (131030) | Number not in Meta's sandbox test list | Add the number to your test recipients in Meta Business Suite |
| "Account locked" (131031) | Meta suspended the WhatsApp Business account | Contact Meta support to resolve |
Inbox
Requires Inbox add-on -- Build: +$10/mo, Accelerate: +$50/unit, Unlimited: +$1,000/mo
WhatsApp DM conversations are available through the unified Inbox API, which aggregates conversations across all supported platforms.
Direct Messages
| Feature | Supported |
|---|---|
| List conversations | Yes |
| Fetch messages | Yes |
| Send text messages | Yes |
| Send attachments | Yes (images, videos, documents, audio) |
| Archive/unarchive | Yes |
Attachment Support
| Type | Supported | Max Size |
|---|---|---|
| Images | Yes | 5 MB |
| Videos | Yes | 16 MB |
| Documents | Yes | 100 MB |
| Audio | Yes | 16 MB |
Webhooks
Subscribe to message.received to get notified when new WhatsApp messages arrive.
Notes
- 24-hour window: Free-form messages only within 24 hours of customer's last message
- Templates: Required to initiate or re-initiate conversations outside the window
- Delivery tracking: Messages have sent/delivered/read status updates via webhooks
See Inbox API Reference for endpoint details.
Related Endpoints
- Bulk Send - Send template messages to multiple recipients
- Broadcasts - Create and manage broadcast campaigns
- Templates - Manage message templates
- Contacts - Manage contact lists
- Connect via Credentials - Headless WhatsApp connection
- Inbox - View and manage DM conversations (requires Inbox add-on)