How to Send WhatsApp Messages with an API: Node.js, PHP and cURL
Send your first WhatsApp message from code in a few minutes, receive replies with webhooks, and test everything on a simulated device before you link a real number.
Table of contents
Order confirmations, one-time codes, appointment reminders, delivery updates: if your app already sends email or SMS, WhatsApp is usually where customers actually read the message. This guide shows the shortest path from an API key to a delivered WhatsApp message, and how to receive replies.
How it works
ChatKomodo links your own WhatsApp number as a device. You scan a QR code once, exactly like WhatsApp Web, and the number keeps working on the phone. Your app then calls a REST API, and ChatKomodo sends from that number:
POST /v1/sessions/{device}/messagessends a message: text, image, document, location, poll or contact card;- webhooks tell your server about replies and delivery problems as they happen.
1. Try it without a phone
You can start before linking a real number. A test device behaves like a linked number, but nothing reaches WhatsApp: messages are logged, your webhooks fire, and statuses move from sent to delivered to read.
curl -X POST https://api.chatkomodo.com/v1/sessions \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sandbox": true}'The answer contains the device id. Details are in the test device guide.
2. Send a message
With cURL:
curl -X POST "https://api.chatkomodo.com/v1/sessions/$DEVICE_ID/messages" \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042-shipped" \
-d '{"chat_id": "+447700900123", "type": "text", "body": "Your order #1042 has shipped"}'With the Node.js library:
import { ChatKomodo } from 'chatkomodo';
const ck = new ChatKomodo({ apiKey: process.env.CHATKOMODO_API_KEY });
await ck.messages.send(deviceId, {
chat_id: '+447700900123',
type: 'text',
body: 'Your order #1042 has shipped',
}, { idempotencyKey: 'order-1042-shipped' });With PHP or Laravel:
use ChatKomodo\Laravel\Facades\ChatKomodo;
ChatKomodo::sendText('+447700900123', 'Your order #1042 has shipped', 'order-1042-shipped');The idempotency key matters more than it looks. If your request times out and you retry, the same key returns the first result instead of sending the message twice. Installing the libraries is covered in Libraries.
3. Receive replies with a webhook
Register an HTTPS endpoint and choose the events you want:
curl -X POST https://api.chatkomodo.com/v1/webhooks \
-H "Authorization: Bearer $CHATKOMODO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/webhooks/chatkomodo", "secret": "a-long-random-secret-of-32-characters-or-more", "events": ["message.received", "message.failed"]}'Every delivery is signed with your secret in the X-Webhook-Signature header. Verify it on the raw body before trusting the event. The libraries do it in one line (parseWebhook in Node.js, Webhook::verify or the chatkomodo.webhook middleware in PHP/Laravel). With a test device you can play the customer yourself: POST /v1/sessions/{device}/sandbox/inbound makes a "customer" write to you, and your webhook receives it.
4. Things to get right before going live
- Format numbers internationally:
+44…,+62…,+1…. Local numbers work for your business's own country. - Respect the pace: messages are sent with human-like pauses, and a newly linked number sends more slowly for its first day. Bulk sends belong in a broadcast, not a tight loop.
- Handle 429: when you exceed your key's request limit, the API answers
429withRetry-After. The libraries wait and retry automatically. - Opt-outs: recipients who reply STOP are blocked from further messages. That is on purpose.
Next steps
- Quickstart: from sign-up to a delivered message.
- Sending messages: media, polls, statuses and pacing.
- Webhooks: events, retries and signature checks.
- The Postman collection has every endpoint with an example.