> ## Documentation Index
> Fetch the complete documentation index at: https://docs.custral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# widget.keyword.matched

> A website visitor sent a message in a widget chat. Fires for every message.

Fires for **every** message a visitor sends in a widget chat.

<Warning>
  **Despite the name, nothing is matched.** Keywords are a setting on the
  workflow trigger of the same name, and they are applied only there. A webhook
  subscription receives every visitor message, and the payload does not say
  which keyword, if any, the message contained. Filter on `text` in your
  handler.
</Warning>

## Payload

```json theme={null}
{
  "id": "whd_1Ns7cX4gT",
  "event": "widget.keyword.matched",
  "createdAt": "2026-09-11T13:06:40.519Z",
  "data": {
    "conversationId": "conv_7Yn2kP",
    "visitorId": "wv_5Hc2pR",
    "text": "Can I cancel my plan before it renews?"
  }
}
```

<ResponseField name="conversationId" type="string" required>
  The chat the message was sent in. Always present.
</ResponseField>

<ResponseField name="visitorId" type="string">
  The visitor who sent it.
</ResponseField>

<ResponseField name="text" type="string">
  The message, with leading and trailing whitespace removed.
</ResponseField>

## Worth knowing

* **Visitor messages only.** Replies from your team and from the answer bot do
  not fire this.
* **One event per message.** A busy widget delivers a lot of these, so filter
  early and keep the handler fast.
* **No message id.** To read the message in its thread, fetch the conversation
  from the [Conversations API](/dev/api-reference/conversations).

## Example

```ts theme={null}
const RETENTION_RISK = /\b(cancel|refund|downgrade)\b/i;

custral.on("widget.keyword.matched", async (event) => {
  const {conversationId, text} = event.data;
  if (!RETENTION_RISK.test(String(text ?? ""))) return;

  await alerts.notify({team: "retention", conversationId});
});
```


## Related topics

- [Widget](/dev/webhooks/events/widget.md)
- [widget.conversation.started](/dev/webhooks/events/widget-conversation-started.md)
- [Webhooks](/dev/webhooks/overview.md)
- [Event catalog](/dev/webhooks/events/overview.md)
- [Install the widget](/comms/chat/widget-install.md)
