Guides/For developers
I already have my own AI, I just need the WhatsApp side
WACO becomes the official WhatsApp interface, the brain stays yours: messages in by webhook, replies out by API.
3 min read
People arrive with the same sentence: the AI assistant already works, I just need WhatsApp. That is one of the most straightforward ways to use WACO. Our built-in AI Operator does not need to be switched on, and there is no AI cost from us. You use the official number, the webhook, and the API.
Three pieces
- Incoming message is posted by WACO to your URL as
message.received. - Your AI thinks on your own infrastructure.
- The reply goes out through
POST /api/v1/balas.
Set up once
- An API key
waco_…from the Settings page. - Your webhook URL (HTTPS required) on the Developer page. WACO shows a
whsec_…secret once; keep it, you need it to verify signatures.
A complete example, Node and Express
const express = require('express');
const crypto = require('crypto');
const app = express();
// The signature is computed over the RAW body, so do not use express.json() on this route.
app.post('/waco', express.raw({ type: '*/*' }), async (req, res) => {
const expected = 'sha256=' + crypto.createHmac('sha256', process.env.WACO_WEBHOOK_SECRET)
.update(req.body).digest('hex');
const got = String(req.get('X-WACO-Signature') || '');
if (got.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) {
return res.sendStatus(401);
}
// 1) Answer 200 FIRST. A slow AI must not hold the webhook.
res.sendStatus(200);
const ev = JSON.parse(req.body.toString('utf8'));
if (ev.event !== 'message.received') return; // ignore status and echo
if (alreadySeen(ev.pesan.id)) return; // deliveries can repeat
// 2) Queue per number so one customer is handled in order
queue(ev.dari, async () => {
const answer = await yourAI(ev.dari, ev.pesan.teks); // your own brain
// 3) Send the reply back through WACO
await fetch('https://waco.id/api/v1/balas', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.WACO_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ nomor: ev.dari, teks: answer }),
});
});
});
app.listen(3000);
Four things that surprise people
- Answer 200 first, think afterwards. If you wait for the AI before replying 200, the delivery counts as failed and is retried, and the customer can get a double answer.
- The 24-hour window is Meta's. More than 24 hours after the customer's last
message, free text is refused and you must use a template. Templates can be created through
POST /api/v1/template, but still wait for Meta's approval. - Media arrives as an ID, not a URL. Fetch the file with
GET /api/v1/media/:idif your AI needs to see the image. - Deliveries can repeat. Remember processed
pesan.idvalues for at least a day.
The team inbox can stay on. The same event goes to both places: your AI receives it by webhook, your team still sees the conversation and can take over at any time. Trouble on one side does not hold up the other.
If your AI is an agent
WACO also serves MCP at /mcp, so your agent can call WACO as a tool
(prepare a confirmed blast, read history, send messages) without writing an HTTP integration.
Details in the API guide and the webhook guide. OpenClaw users have their own page.
← PreviousI want incoming chats assigned to a specific person automatically
Next →I run OpenClaw and want WhatsApp done properly, not a QR session that keeps dropping
Still stuck after following this guide? Contact us from the
dashboard — attach a screenshot if you have one, it speeds everything up.