---
title: "Webhooks"
url: https://wajo.ai/docs/webhooks.md
summary: "The MCP endpoint is pull-only: every tool is a request you make, and nothing on it can call you back. A device, a bot or an integration that must react the moment the assistant answers registers a **webhook**: an https URL that receives a signed POST for each event it subscribed to."
related: [getting-started, building-agents, connectors, mcp-server, mcp-tools, sessions]
---

# Webhooks

The MCP endpoint is pull-only: every tool is a request you make, and nothing
on it can call you back. A device, a bot or an integration that must react
the moment the assistant answers registers a **webhook**: an https URL that
receives a signed POST for each event it subscribed to.

## Registering an endpoint

Call `register_webhook` with the `url`, a `name`, and optionally the
`eventKinds` to receive (omit for all). The reply carries the endpoint and
its signing secret **once**; the server keeps the secret encrypted and can
never show it again. Delete and re-register an endpoint to get a new one.

- Up to 5 webhooks per account.
- The URL must be https and publicly reachable. Private addresses, loopback
  and cloud metadata hosts are refused at registration and again at dial.
- Endpoints can also be managed on the Apps page of the Wajo settings.

## Events

Each delivery is a JSON body with `id`, `type`, `occurredAt` (RFC 3339 with
nanoseconds; parse it, never compare the strings, to order the events of one
conversation), `webhookId` and one data object named after the type:

- `conversation.reply` → `reply`: a turn in one of your conversations closed.
  Carries `conversationId` (the same id the conversation tools use), `runId`
  (the run that produced the turn; `get_fo_conversation` turns carry the same
  `runId`), `channel` (`chat`, `email`, `slack`, `voice`, or the texting
  platform: `imessage`, `whatsapp`, `telegram`, `rcs`, `sms`), `status`
  (`completed`, `failed`, `stopped`), `trigger` (`user`, `inbound_email`,
  `inbound_text`, `voice_postcall`, `system`), `agentId`, `text` (the
  assistant's final message, capped at 2,000 characters; a cut text ends in
  `…`), `textTruncated` (true when `text` was cut; read the full turn by
  `runId` with `get_fo_conversation`), `awaitingUser` (true when the
  assistant asked you something) and `closure` (`message`, `delivered`,
  `acknowledged`, `silent`).
- `schedule.fired` → `reply`: a scheduled action or one-shot reminder fired
  and finished. Same shape as above with `trigger: "schedule"` and the
  `scheduleId`.
- `message.proactive` → `message`: the assistant sent you a message on its
  own initiative. Carries `conversationId` (when known), `channel`, `text`
  and `agentId`.
- `webhook.test` → `test`: what `test_webhook` sends. Every endpoint receives
  it.

## Verifying a delivery

Deliveries follow the Standard Webhooks specification. Each request carries
`webhook-id`, `webhook-timestamp` (unix seconds) and `webhook-signature`
(`v1,<base64 HMAC-SHA256>` over `"<id>.<timestamp>.<body>"` keyed with your
secret). Verify with any Standard Webhooks library, reject timestamps older
than five minutes, and deduplicate on `webhook-id`: a retried delivery keeps
the same id.

## Delivery and retries

Answer `2xx` within 10 seconds to acknowledge; do the work after responding.
A `5xx`, a timeout or a connection failure is retried with backoff for up to
an hour; a `4xx` is not retried; `410 Gone` disables the endpoint. An
endpoint whose every attempt fails for three days is disabled too. A
successful `test_webhook` re-enables a disabled endpoint, and `list_webhooks`
shows each endpoint's status, last delivery, last error and failure streak.
