Quickstart
Send your first WhatsApp message and receive your first inbound event.
Quickstart
Send your first WhatsApp message and receive your first inbound event. Five minutes, three steps.
Before you start
You need a connected number. In the dashboard open Connections, scan the
QR code with the WhatsApp account you want to send from, and wait until the
connection reads connected. Copy its connection ID — a UUID you will pass
with every message.
1. Create an API key
Dashboard → Developers → API keys → Create key.
The key is shown once. Store it the way you store a database password: in your secret manager, never in source control, never in front-end code. Anyone holding it can send messages as your workspace.
cap_a1b2c3d4e5f6.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
If you lose it, create a new key and revoke the old one. There is no way to read an existing key back.
2. Send a message
curl -X POST https://your-domain.com/api/v1/messages \
-H 'Authorization: Bearer cap_a1b2c3d4e5f6.xxxx…' \
-H 'Content-Type: application/json' \
-d '{
"connection_id": "bbf1ba6b-be14-491c-9d9f-8d8317f1e542",
"recipient": "+923001234567",
"body": "Hello from MessageVia"
}'
recipient takes a raw phone number in E.164 format. You do not need to create a
contact first — an unknown number is added to your contact list automatically.
{ "data": { "id": "9bbe8e93-…", "direction": "outbound", "status": "queued", "message_type": "text", … } }
A 202 Accepted means we have taken responsibility for the message, not that
WhatsApp has delivered it. See Messages for the difference, which
matters more than it sounds.
3. Receive inbound messages
Replies do not come back on the same connection. They arrive as a webhook: a signed HTTPS POST to a URL you control.
Dashboard → Developers → Webhooks → Add endpoint. Give it an HTTPS URL and
subscribe to message.received.
When someone messages your number, we POST:
{
"id": "ce93fdad-1a30-4a67-a4d6-7c17e513240a",
"type": "message.received",
"api_version": "2026-07-20",
"occurred_at": "2026-07-20T11:06:18+00:00",
"data": {
"message": {
"id": "9bbe8e93-a33a-4221-9466-dc4847786444",
"type": "text",
"body": "Yes, that works",
"attachments": []
},
"contact": { "id": "f57f3c61-...", "phone": "+923001234567", "name": null },
"conversation": { "id": "e207cce6-..." },
"connection": { "id": "bbf1ba6b-...", "name": "Support line" }
}
}
Everything you need is in the event. You do not have to call back to find out what arrived.
Verify the signature before you trust any of it. An unverified webhook endpoint is a public API that anyone can post to. See Signature verification — it is six lines of code, and it is not optional.
No public URL yet?
While building locally, expose your machine with a tunnel:
cloudflared tunnel --url http://127.0.0.1:9099
Register the tunnel's HTTPS URL as your endpoint. We reject plain HTTP, private IP ranges and anything that resolves to a loopback or link-local address, so a tunnel is the practical way to develop against real traffic.
Once registered, use Send test event on the endpoint page to fire a synthetic
message.received at your handler without waiting for a real message. Test
events travel the ordinary delivery path — same envelope, same signature, same
retries — and carry "test": true inside data so you can never mistake one for
a customer message.
Next
- Authentication — keys, scopes, what a rejected request looks like
- Messages — text, media, and what "accepted" really means
- Media — download the photos and documents customers send you
- Webhooks — events, retries, and debugging a failing endpoint