Notifications and integrations
A conversation ends, somebody asks for a person, a visitor leaves an email — and you want to know without watching a dashboard. Every destination is independent, and each one chooses which events it receives and how much of each event it carries.
Two screens, because they are two jobs
Notifications holds email addresses: who on your team gets told. Integrations holds the connections to other products — Slack, Microsoft Teams, Discord, Google Sheets, and webhooks to your own systems. They work identically; they are apart because “who should hear about this” and “where does this Slack URL come from” are different questions with different answers.
The five events
These are the values that appear in the event field of every payload. They do not change, and nothing else is ever sent.
| Event | Fires when |
|---|---|
chat_summary | A conversation went quiet and closed. Shown in the app as “Chat ended”. |
escalation | A visitor asked for a person, or the bot handed over. |
lead | A visitor left a name, email or phone number. |
unanswered | The bot searched your documents and found nothing to answer with. |
agent_reply | Somebody on your team answered a thread. |
visitor_reply | The visitor answered on their ticket, so it is back with you. |
Choosing what each event carries
A destination holds one rule per event, and each rule says what that event includes. This is the part worth setting deliberately: it is how a spreadsheet of captured contacts can hold names and email addresses while your engineering webhook, subscribed to the same account, never sees either.
| Group | Adds |
|---|---|
| Summary | summary, and escalationReason where there is one |
| Contact details | visitorName, visitorEmail, visitorPhone |
| Location and device | visitorCity, visitorCountry, visitorIp, visitorUserAgent |
| Full transcript | messages — every turn, up to 200 |
An omitted group means those keys are absent from the JSON rather than null. A receiver checking if (payload.visitorEmail) behaves the same either way, and one that logs the payload does not end up with a column of nulls implying you had nothing when you simply were not asked.
What a webhook receives
A POST with a JSON body. The fields above the line are always present; the rest depend on the rule.
POST https://example.com/hooks/atal
Content-Type: application/json
X-Atal-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015…
{
"event": "lead",
"conversationId": "3f0c…",
"botId": "a91b…",
"botName": "Acme Support",
"chatUrl": "https://app.example.com/conversations/3f0c…",
"messageCount": 6,
"startedAt": "2026-09-07T09:12:04.115Z",
"lastMessageAt": "2026-09-07T09:18:41.902Z",
"sentAt": "2026-09-07T09:18:47.336Z",
"summary": "Asked about weekend delivery and left an address.",
"visitorName": "Priya Nair",
"visitorEmail": "[email protected]",
"visitorPhone": null
}Verifying the signature
Plain webhooks are signed with a secret shown once when you create the destination. The header is X-Atal-Signature and its value is sha256= followed by the hex HMAC-SHA256 of the raw request body. Sign the bytes you received, not a re-serialised object — key order and whitespace both change the digest.
import { createHmac, timingSafeEqual } from 'node:crypto';
function valid(rawBody, header, secret) {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(header ?? '');
return a.length === b.length && timingSafeEqual(a, b);
}Slack, Teams and Discord are not signed. They accept an incoming-webhook URL that cannot verify an HMAC and would ignore the header, so there the URL is the credential — treat it as a secret, and rotate it in that product if it leaks.
When a delivery fails
- Each attempt is recorded with its response code and error. Open Recent deliveries on the destination to see the last twenty.
- Failures are retried. After twenty consecutive failures the destination stops being selected, and the screen says so — fix the endpoint and press Resume.
- Test sends one delivery now, using a real recent conversation rather than a fixture. What arrives is what a real notification looks like, which is the only version worth testing against.
Google Sheets
Paste the spreadsheet link and you are sent to Google to grant access to that one spreadsheet — the scope requested is spreadsheets, never drive, so nothing else in your account is readable. Each event appends a row to the tab you name. The column headings to paste into row 1 are on the panel itself.
A note on order
Nothing is delivered for a bot that has no knowledge yet, because such a bot does not open to visitors at all — see Install the widget. Add a document first and the rest follows.