A webhook is a URL you own. You tell Lynomia Chat which events you care about, and each time one happens we POST a JSON body to that URL. It is the way to make something outside the product react to what happens inside it.
When to use it
When another system needs to know. A ticketing tool that should open a case when a conversation is created, a dashboard that counts incoming messages, an automation tool that starts a workflow when a contact is added.
When not to use it
- To send messages or change anything. Webhooks are outbound only. Use the API for writes.
- To answer a conversation in line. Whatever your endpoint returns is discarded. Nothing it sends back enters the conversation.
- For one specific rule. If you only want a POST when a particular condition is met, the webhook action in an automation rule or a flow is narrower and cheaper than subscribing to every message in the account.
What you need first
An administrator account, and an endpoint that is reachable from the public internet over HTTP or HTTPS and answers within a few seconds. On some plans, Webhooks shows an upgrade notice instead of the list.
Steps
- Go to Settings → Integrations → Webhooks and choose Add new webhook.
- Enter the URL, give it a name, and tick the events you want. At least one event is required.
- Save. The secret is shown once on the confirmation screen; copy it then. You can also reveal it later in the webhook's edit form.
The events
Ten events are offered in the form. An eleventh, inbox_updated, appears only if your installation has inbox
events switched on.
| Event | Fires when |
|---|---|
conversation_created |
a conversation is opened |
conversation_updated |
any attribute of a conversation changes |
conversation_status_changed |
its status changes |
message_created |
a message is added |
message_updated |
a message changes |
contact_created |
a contact is added |
contact_updated |
a contact's attributes change |
webwidget_triggered |
a visitor opens the live chat widget |
conversation_typing_on / conversation_typing_off |
someone starts or stops typing |
inbox_updated |
an inbox's settings change |
Two things to expect. conversation_updated is noisy — it fires on changes you would not call an update, so
filter on changed_attributes rather than treating every one as news. And only incoming, outgoing and template
messages are sent; private notes and activity lines are not.
The payload
Every body is a JSON object with an event key naming the event, plus the object the event is about.
| Event group | Body carries |
|---|---|
| Message events | the message — id, content, content_type, message_type, private, source_id, attachments when present — plus its conversation, inbox, sender and account |
| Conversation events | the conversation — id, status, priority, labels, inbox_id, meta with sender, assignee and team, custom_attributes, timestamps — its newest message, and account |
| Contact events | the contact — id, name, email, phone_number, identifier, avatar, custom_attributes, additional_attributes, blocked — and account |
The three "changed" events — conversation_updated, conversation_status_changed and contact_updated — add a
changed_attributes array describing what moved. contact_updated and inbox_updated are not sent at all when
nothing changed.
Verifying a delivery
Each webhook has its own secret, and signed deliveries carry three request headers: a delivery id, a timestamp,
and a signature. The signature's value is sha256= followed by the HMAC-SHA256 of the timestamp, a full stop, and
the raw request body, keyed with the secret:
sha256=HMAC_SHA256(secret, "<timestamp>.<raw body>")
Compute it over the bytes you received, not over a re-serialised copy, and compare in constant time. Reject anything that does not match, and treat the delivery id as the key for ignoring a repeat.
The three headers are named exactly:
| Header | Carries |
|---|---|
X-Chatwoot-Signature |
sha256= followed by the HMAC above |
X-Chatwoot-Timestamp |
the timestamp the signature was computed over |
X-Chatwoot-Delivery |
the delivery id, when one is available |
Those names are part of the wire format and do not change with the installation's branding.
When a delivery fails
This is the part worth reading twice. A webhook delivery is attempted once.
| What happens | |
|---|---|
| Your endpoint is slow | the request is abandoned after the installation's webhook timeout, five seconds by default |
| Your endpoint answers 4xx or 5xx | the delivery is counted as failed |
| Your endpoint is down | the delivery is counted as failed |
| Any of the above | the failure is written to the server log. Nothing is retried, nothing is queued, and nothing is shown to you |
So events that occur while your endpoint is unavailable are gone. If you cannot afford to lose them, accept the POST into a queue of your own, answer immediately, and process it afterwards.
Who can do this
Administrators only. The custom role permission list has no entry for webhooks, so it cannot be delegated — see roles and permissions.
Limits
- No retries, no delivery history, no replay, and no test button. You verify a webhook by triggering a real event.
- One webhook per URL per account. To get the same events twice, use two URLs.
- Private and internal addresses are refused. A URL resolving to localhost or a private network is not delivered to. Your endpoint has to be publicly reachable.
inbox_createdcannot be subscribed to in the form. It exists in the API's event list only.- The response body is never read, and the response's content type is not checked.
- The payload is not configurable. You cannot add fields, remove fields or choose a format.
- Subscriptions are account-wide. You cannot subscribe to one inbox's messages only; filter by
inboxin your own code.
Other things that POST to a URL
Webhooks are one of several. They are not interchangeable, and only some are signed.
| Sent by | event in the body |
Signed |
|---|---|---|
| A webhook subscription | the event you subscribed to | yes, with the webhook's secret |
| The webhook action in an automation rule | automation_event.<trigger>, with a commerce block on a Commerce trigger |
no |
| The webhook action in a macro | macro.executed |
no |
| The webhook node in a flow | flow_webhook, with the flow, session and the values the flow collected |
yes, with the flow bot's secret |
Related
- Integrations
- Automation rules · Macros · Flow builder
- Roles and permissions
- When something is not working
If it does not work
Nothing arrives. Check in this order: the endpoint is publicly reachable, it answers within five seconds, and the event you expect is actually ticked. A URL on a private network never receives anything.
Some events arrive and others do not. Private notes and activity lines are not sent as messages, and
contact_updated is suppressed when nothing changed.
My signature check fails. You are almost certainly hashing a re-serialised body. Hash the raw bytes, and include the timestamp and the full stop before them.
I lost events during an outage. There is no backfill. Read the API for the period you missed.
The Webhooks page shows an upgrade notice. API and webhooks are not included on your plan.