Skip to content
Chatto v0.5.0

Bot Webhook Reference

Configure an endpoint with Bots & Webhooks. The receiving tool gets message text and account, room, and message identifiers. Use a tool whose data policy permits this access.

{
"version": 1,
"id": "stable-delivery-id",
"type": "message.created",
"triggers": ["direct_message", "mention"],
"occurred_at": "2026-09-05T12:00:00Z",
"bot_id": "bot-id",
"room_id": "room-id",
"thread_root_id": null,
"message": {
"id": "message-id",
"author_id": "author-id",
"body": "Hello @helper_bot"
}
}

Use triggers to route mentions and DMs. thread_root_id is null for a root message. The body does not contain attachments. Chatto does not change this format for specific tools.

Return HTTP 2xx when you accept the request. Do long tasks separately, and reply with the bot API key through the normal API. Chatto ignores the response body.

Chatto adds these headers:

  • Chatto-Webhook-Id: the delivery ID from the body.
  • Chatto-Webhook-Timestamp: Unix time in seconds for this attempt.
  • Chatto-Webhook-Signature: v1= followed by a hexadecimal HMAC-SHA256.

To verify a request:

  1. Use the signing secret as its displayed UTF-8 text. Do not Base64-decode it.
  2. Compute HMAC-SHA256 over the timestamp, a full stop (.), and the exact request body bytes.
  3. Compare the result in constant time.
  4. Reject old timestamps, for example older than five minutes.
  5. Deduplicate by delivery ID. A retry has the same ID and a new timestamp.

If your tool cannot verify signatures, authenticate with a secret URL or the Authorization header instead.

Chatto retries transport failures and all non-2xx responses. It does not follow redirects. By default, Chatto makes up to five attempts within 24 hours of the message. The delay starts at 30 seconds and doubles up to 30 minutes. Each request has a 10-second timeout. To change these values, see Outbound bot webhooks.

Delivery is best effort. A server restart discards pending requests and retries without a failure record. Requests can arrive more than once or out of order, so use the delivery ID to prevent repeated work.

Before each attempt, Chatto checks the current access of the bot. It skips the request if the bot lost access, the message is unavailable, or the endpoint is paused or revoked. The body contains the current message text, so an edit can change it between attempts.

Select Recent failures on an endpoint to see failed deliveries with time, attempt count, HTTP status, and failure category. Chatto keeps failures for seven days; change this with core.log.retention. Chatto does not record successes, so an empty list does not prove that delivery succeeded.

To react to all activity that the bot can see, use the normal realtime subscription with LIVE_ONLY. There is no bot-only channel. See Realtime and the RealtimeEvent reference.

To react only when Chatto would notify the bot, read NotificationService.ListNotificationOccurrences and filter by cause:

CauseWhen Chatto creates it
direct_message_receivedAnother account sends a message in a DM with the bot.
direct_mention_receivedAnother account mentions the bot directly in a channel room.
reply_receivedAnother account replies to a message from the bot.
followed_thread_activityAnother account posts in a thread that the bot follows.

Ignore other causes. A delivered direct mention also makes the bot follow the thread, so later replies arrive as followed_thread_activity. An explicit unfollow stays in effect.

Save the IDs of processed occurrences, because one occurrence can appear more than once. One message can create more than one cause. To act only once for each message, also deduplicate by event_id. After a disconnect, read the list again to recover missed occurrences within the notification retention period. Then load the message or thread, for example with ThreadService.GetThreadEvents.