Webhook event catalog
Every event kirim.dev publishes, its source (Meta vs kirim.dev), and the payload shape. → /webhooks/events/
A customer messages your business WhatsApp number. Within a second:
You write zero backend code. Everything lives in an n8n workflow on top
of @kirimdev/n8n-nodes-kirim.
If you’re already on Make / Zapier / Pipedream, the same pattern applies — substitute their Kirim integration. The fallback at the end of this page is for teams who’d rather own the code.
┌─────────────────┐ │ Customer WA │ └────────┬────────┘ │ message.received ▼ ┌────────────────────┐ │ Kirim Trigger │ ← n8n node, HMAC verified │ (event: msg.recv) │ └────────┬───────────┘ │ ▼ ┌────────────────────┐ │ AI Agent node │ → returns JSON { intent, reply } │ (OpenAI/Anthropic)│ └────────┬───────────┘ │ ▼ ┌────────────────────┐ │ Switch on intent │ └─┬───────┬────────┬─┘ │ │ │ ▼ ▼ ▼ question complaint small_talk │ │ │ ▼ ▼ ▼ Send Update Send Text Template Conv (AI reply) (assign + label)One trigger in, three exits out. Every branch ends with a Kirim node call so the customer always gets a response (or a human gets pinged).
Install the community node.
In your n8n instance: Settings → Community Nodes → Install →
enter @kirimdev/n8n-nodes-kirim → confirm. Wait for the install
to finish (n8n restarts the node runtime automatically).
Add a Kirim API credential.
Credentials → New → Kirim API. Paste your kdv_live_… key.
Save. The same credential is used by both the Kirim Trigger and
the Kirim action nodes.
Add the Kirim Trigger node.
Drag Kirim Trigger onto the canvas as the workflow’s start node. Configure:
message.received106540352242922 (or * to receive from
every connected number in the org)Click Listen for Test Event and send yourself a test WhatsApp message. You should see the raw event arrive — that confirms the subscription is wired and HMAC verification passed.
Add the AI Agent node.
Drag AI Agent (or OpenAI / Anthropic if you want a single-shot call) after the trigger. System prompt:
You are a WhatsApp support triage classifier for an Indonesianecommerce store.
Classify the incoming message as exactly one of: - "question" — customer asking about product, pricing, or service - "complaint" — customer has a problem (broken item, late delivery, refund) - "small_talk" — greeting, thanks, off-topic chatter
Respond with strict JSON, no prose:{ "intent": "question" | "complaint" | "small_talk", "reply": "<a one-sentence friendly Bahasa Indonesia reply suitable for small_talk; empty string otherwise>"}User prompt: {{ $json.data.messages[0].text.body }} (n8n’s
expression syntax pulls the inbound text from the Meta-shaped
payload).
Add a Switch node.
Route on {{ $json.intent }} with three rules:
| Condition | Output |
|---|---|
equals "question" | → Output 0 |
equals "complaint" | → Output 1 |
equals "small_talk" | → Output 2 |
Branch — question → send template auto-reply.
Drag a Kirim node onto output 0. Operation: Message → Send Template.
{{ $('Kirim Trigger').item.json.phone_number_id }}{{ $('Kirim Trigger').item.json.data.messages[0].from }}auto_reply_questionid_IDauto_reply_question is a pre-approved template along the lines
of “Halo! Tim kami akan membalas dalam 1 jam kerja. Sementara
itu, FAQ lengkap di {{1}}.” with one URL parameter.
Branch — complaint → assign to a human.
Drag a Kirim node onto output 1. Operation: Conversation → Update.
{{ $('Kirim Trigger').item.json.data.conversation_id }}usr_01HXSUPPORTLEADUSERID (a member of your
support team)openThen a second Kirim node — Conversation → Add Label:
auto-triagedThe human picks the conversation up from the kirim.dev inbox UI; they see the inbound message plus the AI’s classification stored as a label.
Branch — small_talk → send AI reply as text.
Drag a Kirim node onto output 2. Operation: Message → Send Text.
{{ $('Kirim Trigger').item.json.data.messages[0].from }}{{ $('AI Agent').item.json.reply }}This works because small-talk happens inside the 24-hour conversation window — the customer literally just messaged you, so a free-form text reply is allowed (see Send text).
Add a final Label step for audit.
On every branch, add a Kirim Conversation → Add Label with
label auto-triaged. You can then filter the inbox by that label
in the dashboard to spot-check the bot’s decisions.
Activate.
Toggle the workflow to Active. n8n persists the webhook subscription with kirim.dev; deliveries now flow continuously.
A trimmed export of the key connections — drop into n8n via Import from JSON as a starting point, then add your credentials and template names:
{ "name": "WA Support Triage", "nodes": [ { "name": "Kirim Trigger", "type": "@kirimdev/n8n-nodes-kirim.kirimTrigger", "parameters": { "event": "message.received", "phoneNumberId": "106540352242922" }, "credentials": { "kirimApi": "Kirim Prod" } }, { "name": "AI Triage", "type": "@n8n/n8n-nodes-langchain.agent", "parameters": { "systemMessage": "You are a WhatsApp support triage classifier...", "responseFormat": "json" } }, { "name": "Switch on intent", "type": "n8n-nodes-base.switch", "parameters": { "rules": [ { "value": "={{$json.intent}}", "operation": "equals", "compareValue": "question" }, { "value": "={{$json.intent}}", "operation": "equals", "compareValue": "complaint" }, { "value": "={{$json.intent}}", "operation": "equals", "compareValue": "small_talk" } ] } } ], "connections": { "Kirim Trigger": { "main": [[{ "node": "AI Triage", "type": "main", "index": 0 }]] }, "AI Triage": { "main": [[{ "node": "Switch on intent", "type": "main", "index": 0 }]] } }}Prefer code? The same flow with @kirimdev/sdk and any HTTP server.
import { Hono } from 'hono'import { Kirim } from '@kirimdev/sdk'import { verifyWebhookSignature } from '@kirimdev/sdk/webhooks'import OpenAI from 'openai'
const kirim = new Kirim({ apiKey: process.env.KIRIM_KEY! })const openai = new OpenAI()const PHONE_NUMBER_ID = '106540352242922'const SUPPORT_USER_ID = 'usr_01HXSUPPORTLEADUSERID'
const SYSTEM = `You are a WhatsApp support triage classifier...Respond with strict JSON: {"intent":"question|complaint|small_talk","reply":""}`
const app = new Hono()
app.post('/kirim-hook', async (c) => { const raw = await c.req.text() const ok = await verifyWebhookSignature( raw, c.req.header('x-kirim-signature'), process.env.KIRIM_WEBHOOK_SECRET!, ) if (!ok) return c.text('bad signature', 401)
const event = JSON.parse(raw) if (event.type !== 'message.received') return c.text('ok')
const inbound = event.data.messages?.[0] if (!inbound?.text?.body) return c.text('ok')
// 1. Triage with the LLM. const ai = await openai.chat.completions.create({ model: 'gpt-4o-mini', response_format: { type: 'json_object' }, messages: [ { role: 'system', content: SYSTEM }, { role: 'user', content: inbound.text.body }, ], }) const { intent, reply } = JSON.parse(ai.choices[0].message.content!)
const phone = kirim.phoneNumbers(PHONE_NUMBER_ID)
// 2. Route. if (intent === 'question') { await phone.messages.send({ messaging_product: 'whatsapp', to: inbound.from, type: 'template', template: { name: 'auto_reply_question', language: { code: 'id_ID' }, components: [ { type: 'body', parameters: [{ type: 'text', text: 'https://shop.example/faq' }] }, ], }, }) } else if (intent === 'complaint') { await kirim.conversations.update(event.data.conversation_id, { assigned_user_id: SUPPORT_USER_ID, status: 'open', }) } else { await phone.messages.send({ messaging_product: 'whatsapp', to: inbound.from, type: 'text', text: { body: reply || 'Halo! 👋' }, }) }
// 3. Audit label. await kirim.conversations.addLabel(event.data.conversation_id, { label: 'auto-triaged' })
return c.text('ok')})
export default appcurl -X POST "https://api.kirim.dev/v1/webhook_subscriptions" \ -H "Authorization: Bearer $KIRIM_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-bot.example/kirim-hook", "events": ["message.received"] }'# Response includes signing_secret — store it. Shown ONCE.Webhook event catalog
Every event kirim.dev publishes, its source (Meta vs kirim.dev), and the payload shape. → /webhooks/events/
Webhook signing
HMAC scheme, header names, replay protection. → /webhooks/signing/
Conversation API
Assign, label, resolve — the inbox primitives the bot calls. → /api/
n8n nodes GitHub
Source, issues, contributing. → github.com/kirim-dev/n8n-nodes-kirim