Getting Started
Authentication
All API requests require a Bearer token in the Authorization header:
Authorization: Bearer <YOUR_API_KEY>
API keys are scoped to an organization. You receive one when completing the onboarding flow (see the Onboarding Links guide).
Error responses for invalid or missing authentication:
401 Unauthorized— Missing or invalid API key403 Forbidden— API key doesn't have access to the requested resource
Core Concepts
Data Model
Organization
├── API Keys
├── Onboarding Links
├── Shared Inbox Links
├── Outgoing Webhooks
└── WABAs (WhatsApp Business Accounts)
└── Phone Numbers
└── Conversations
└── Messages
- Organization — Your top-level account. All resources belong to an organization.
- WABA — A WhatsApp Business Account from Meta. Each WABA has its own access token and can have multiple phone numbers.
- Phone Number — A WhatsApp phone number connected to a WABA. Messages are sent and received through phone numbers.
- Conversation — A thread between a phone number and a contact (identified by their phone number).
- Message — A single message within a conversation (inbound or outbound).
- Shared Inbox Link — A public URL bound to one phone number's inbox with read/reply permissions.
IDs
The API uses two kinds of IDs:
| ID Type | Format | Example |
|---|---|---|
| Internal IDs | UUID v4 | 550e8400-e29b-41d4-a716-446655440000 |
| Meta IDs | Numeric string | 123456789012345 |
Most endpoints use internal UUIDs. Meta IDs appear in webhook payloads and when interfacing with Meta's Graph API.
Quick Start
1. Connect a WhatsApp Number
Create an onboarding link and send it to your end-user:
curl -X POST https://api.example.com/v1/onboarding-links \
-H "Authorization: Bearer sk_org_..." \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "redirectUrl": "https://myapp.com/callback"}'
Send the hostedUrl from the response to your end-user. They complete Meta's Embedded Signup in the browser. See the Onboarding Links guide for the full flow.
2. Check Phone Health
Before sending production traffic, fetch the phone health snapshot:
curl https://api.example.com/v1/phones/{phoneId}/health \
-H "Authorization: Bearer sk_org_..."
Look at data.capabilities.canReceiveMessages:
truemeans the platform currently considers the phone ready to receive inbound messagesfalsemeans the phone is not fully ready yet, even if it is already linked to a WABA
This endpoint also triggers an asynchronous resync against Meta in the background. If the local state was stale, a subsequent call will reflect the refreshed values.
3. Send a Message
Before sending outbound traffic, confirm data.capabilities.canSendMessages is true:
curl -X POST https://api.example.com/v1/phones/{phoneId}/messages \
-H "Authorization: Bearer sk_org_..." \
-H "Content-Type: application/json" \
-d '{"type": "text", "to": "5491155551234", "text": "Hello!"}'
See the Sending Messages guide for all message types.
4. Optionally Expose a Shared Inbox
If you want an external user to work inside the inbox UI without giving them your org API key, create a shared inbox link:
curl -X POST https://api.example.com/v1/shared-inbox-links \
-H "Authorization: Bearer sk_org_..." \
-H "Content-Type: application/json" \
-d '{
"phoneNumberId": "660e8400-e29b-41d4-a716-446655440001",
"name": "Support access",
"permissions": {
"readInbox": true,
"readDetails": true,
"markRead": true,
"sendText": true,
"sendMedia": true,
"sendTemplates": true,
"sendAdvanced": false
}
}'
Send the publicUrl from the response to the external user. See the Shared Inbox Links guide for permissions, expiry, rotation, and revoke flows.
5. Receive Messages via Webhooks
Register a webhook to get notified of incoming messages and status updates:
curl -X POST https://api.example.com/v1/outgoing-webhooks \
-H "Authorization: Bearer sk_org_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/whatsapp",
"events": ["message.received", "message.sent"],
"secret": "whsec_your_secret_key"
}'
See the Webhooks guide for payload formats and signature verification.
Phone Readiness
The platform stores two related but different fields for each phone number:
| Field | Meaning |
|---|---|
isRegistered |
The phone has been registered with WhatsApp / Cloud API |
capabilities.canReceiveMessages |
The phone is ready for inbound traffic with the current app subscription and no blocking health issues |
capabilities.canSendMessages |
The phone is healthy enough for outbound messaging |
capabilities.canSendTemplates |
The phone is healthy enough for outbound template messaging |
Use the capability that matches your integration. For inbound-only flows, canReceiveMessages is usually the right operational flag.
Response Format
All responses follow a consistent envelope:
Success (single resource):
{
"data": { ... }
}
Success (list):
{
"data": [ ... ],
"meta": {
"pagination": {
"page": 1,
"limit": 50,
"total": 120,
"hasMore": true
}
}
}
Error:
{
"error": {
"message": "Resource not found",
"code": "NOT_FOUND"
}
}
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
VALIDATION_ERROR |
400 | Invalid request body or parameters |
UNAUTHORIZED |
401 | Missing or invalid API key |
FORBIDDEN |
403 | API key doesn't have access |
NOT_FOUND |
404 | Resource not found |
INTERNAL_ERROR |
500 | Unexpected server error |