Skip to main content
Outbound webhooks are HTTP calls Keebai makes to your server when something happens in your account — an inbound message, a delivery receipt, a CRM ticket moving through the pipeline, an appointment being cancelled. They’re the flip side of the API: instead of you polling, Keebai pushes the event to you. There are 32 events across five domains — WhatsApp, Instagram, conversations, CRM, and scheduling. The full list with payloads is in the event catalog.
Not to be confused with the inbound webhooks Meta sends to Keebai. Those are configured by Keebai in the Meta dashboard when you connect a channel via Embedded Signup; you don’t touch them.

Which direction do you need?

Three different things in Keebai are called a webhook. Picking the wrong one costs an afternoon, so: Note the path collision: /v1/webhooks (plural) is the subscription CRUD on this page, and /v1/webhook/events (singular) is the inbound ingest. They are unrelated surfaces with different scopes.

Use cases

Sync messages to your CRM

Receive every whatsapp.message.received and store it in your system along with the conversation, contact, and content.

Track delivery / read

Subscribe to whatsapp.message.delivered / .read / .failed to report delivery rates in your dashboard.

React to templates

Receive whatsapp.template.status_updated when Meta approves or rejects a template, and notify your marketing team.

Automated provisioning

Listen for whatsapp.channel.connected to trigger your onboarding flow the moment a channel is ready.

Mirror the CRM pipeline

Subscribe to crm.ticket.stage_changed and crm.ticket.closed to keep an external CRM, a BI warehouse, or a Slack channel in step with the funnel.

Sync the calendar

scheduling.appointment.created / .rescheduled / .cancelled carry the branch, service, professional, and customer inline — enough to update an external calendar without a follow-up call.

Escalation alerts

conversation.escalated_to_human fires on the transition, once, when a ticket lands in a human-attended stage. Page whoever needs to pick it up.

Scopes

Rotating a secret invalidates the old one immediately, so webhooks:manage is the scope that can break your own receiver. A dashboard that only inspects delivery history needs webhooks:read alone.

How it works

1

Register an HTTPS URL

Via CLI (keebai webhooks new) or via API (POST /v1/webhooks). Specify the events you care about and optionally extra headers.
2

Keebai returns a secret

Once. You use it to verify that each delivery actually came from us (HMAC-SHA256 signature). It cannot be retrieved later — store it in your secret manager.
3

When an event happens, we deliver it

POST call to your URL with the standard JSON envelope and signature headers. If your endpoint responds 2xx, we mark the delivery as successful. If it responds 5xx / timeout / 408 / 429, we retry with exponential backoff (5 attempts, up to ~3h).
4

After 50 consecutive failures, we auto-disable

To avoid hammering an endpoint that’s been down for a long time. You receive an email and the subscription is left with is_active: false and disabled_reason: 'excessive_failures'. Reactivate via PATCH.

Envelope

Every event shares the same outer shape. The difference is the data field, discriminated by type.
dev_tenant_id is an internal identifier and ships only on the inbound-message and conversation events for channels onboarded through the reseller flow. It is documented here because it is on the wire today, not because it is part of the contract: do not persist it, do not key on it, and expect it to disappear. If you need a stable handle for a tenant, use dev_tenant_external_id — the value you chose yourself.

Delivery headers

Every POST to your endpoint includes:

Guarantees and limitations

At-least-once delivery

If your endpoint times out or returns 5xx, we retry. Your endpoint must be idempotent: dedup by event_id.

No ordering guarantee

Events from the same channel can arrive out of chronological order (especially with retries). Use occurred_at to sort.

10s timeout

If your endpoint doesn’t respond within 10 seconds, we count a timeout and retry. Process fast and return 2xx; if you need heavy work, push it to your own queue.

HTTPS required

We don’t accept http:// URLs. Use a valid certificate (Let’s Encrypt works).

Subscribe narrowly

A subscription lists the events it wants, and the temptation is to take everything “just in case”. Resist it: whatsapp.message.received alone can be thousands of deliveries a day on a busy account, and every one of them costs you a request to process and an idempotency check. Subscribe to what you act on. Add events later — PATCH /v1/webhooks/:id changes the list without rotating the secret or losing history.

Deliveries do not count against your quota

Webhooks are Keebai calling you, so they cost nothing from your rate limit. That is precisely why the quota page recommends webhooks over polling: a poll every minute is 1,440 requests a day and a subscription is zero. Note the asymmetry, though — if you respond to a webhook by calling the API back, those calls do count.

Beware the write-back loop

The most common production incident on this surface is a loop: you subscribe to crm.ticket.updated, react by writing to the ticket, which emits crm.ticket.updated again. It has more entry points than people expect, because writes are indirect. Emitting a CRM event writes its values onto the ticket, so an ingest that fires all day produces ticket webhooks all day. Guard on your side: dedupe by event_id, and skip changes whose resulting state already matches what you were about to write.

Next steps

Event catalog

Full list of type values and the data shape for each one.

Signature verification

How to validate X-Keebai-Signature with HMAC-SHA256 in Node, Python, and PHP.

Manage via CLI

Quickstart with keebai webhooks new/list/test/rotate/delete.

Manage via API

REST endpoints /v1/webhooks to create, list, rotate, delete, and test.