Webhooks
Webhooks tell your backend what happens in conversations: new messages for your search index, analytics, notifications, or an agent that lives on your servers.
Configuration
Section titled “Configuration”[[webhooks]]name = "backend"url = "https://api.example.com/agentim/webhook"secret = "whsec_…" # Standard Webhooks format, at least 16 bytesevents = ["msg.created", "member.*", "report.*"]max_attempts = 8timeout_ms = 5000events takes exact types (msg.created), prefixes (msg.*) or *. Subscribing to member.joined or member.* also delivers members.joined. A new webhook starts from now; it doesn’t replay history.
Guarantees
Section titled “Guarantees”| No loss | The delivery is written to an outbox in the same transaction as the event. What was not committed is never sent; what was committed is sent |
| Order | One delivery task per webhook per database sends events one at a time, in commit order. The next waits until the previous one succeeded |
| At least once | The position moves only after a 2xx answer, and survives restarts. Deduplicate with the webhook-id header |
| Retries | Exponential backoff, up to 5 minutes apart. After max_attempts the event is skipped, audited as webhook.dropped (with its conversation and seq, shown in the message’s trace) and counted in agentim_webhook_dropped_total. Fill the gap from the history endpoint |
Request
Section titled “Request”POST /agentim/webhookwebhook-id: msg_2Ksl…webhook-timestamp: 1792503600webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00content-type: application/json
{"type": "msg.created", "timestamp": 1792503600123, "database": "main", "data": {"conv_id": "c_…", "seq": 42, "event": { … }}}traceparent is the trace of the write that produced the event (Tracing); pass it on to your own services to see the whole chain. It is not part of the signature.
The signature is the Standard Webhooks scheme: base64(HMAC-SHA256(secret, id + "." + timestamp + "." + body)). For a whsec_ secret the HMAC key is the base64-decoded part after the prefix. The official libraries verify it:
import { Webhook } from 'standardwebhooks';const wh = new Webhook(process.env.AGENTIM_WEBHOOK_SECRET);const event = wh.verify(rawBody, req.headers); // throws if the signature or timestamp is wrongVerify against the raw body bytes, before parsing the JSON. Reject timestamps more than five minutes old.
Events
Section titled “Events”Every main-log event type (events), plus user.deleted (an account deletion finished: delete the user in your systems), report.created and report.resolved (notify the reporter and the person concerned).
Friends are outside any conversation, so their events carry the change itself as data: user_id is who acted, other_id the other person.
| Event | When |
|---|---|
friend.requested |
user_id asked other_id (with message) |
friend.accepted |
They became friends: user_id accepted, or asked someone who takes anyone, or your backend set it |
friend.declined |
user_id declined other_id’s request |
friend.withdrawn |
user_id withdrew their request to other_id |
friend.removed |
The friendship ended (user_id removed other_id, or your backend did) |
{"type": "friend.requested", "timestamp": 1792503600123, "database": "main", "data": {"user_id": "u_…", "other_id": "u_…", "message": "Hi, it's Bob from the meetup"}}