Agent skill
Tidio Webhooks Skill
Receive and verify Tidio webhooks. Use when setting up Tidio webhook handlers, debugging x-tidio-signature verification, or handling contact.created, conversation.solved_by_lyro, ticket.created, and customer_satisfaction.rated live chat, Lyro chatbot, and ticketing events.
Install this skill
npx skills add hookdeck/webhook-skills --skill tidio-webhooks
When to Use This Skill
- How do I receive Tidio webhooks?
- How do I verify the
x-tidio-signatureheader on a Tidio webhook? - How do I handle
contact.created,ticket.created, orconversation.solved_events?by_ lyro - Why is my Tidio webhook signature verification failing?
- How do I dedupe Tidio webhooks (
webhook_id) and handle retries?
Verification (core)
Tidio has no webhook SDK; verify manually. The header is x-tidio-signature: t=<unix seconds>,s=<hex>[,s=<hex>...] (one s per active webhook secret). The signature is hex HMAC-SHA256 over the raw body bytes, a literal _, then the t value, keyed with the secret's UTF-8 bytes. The request is valid when any s matches.
const crypto = require('crypto');
function verifyTidio(rawBody /* Buffer */, header, secret) {
let t;
const sigs = [];
for (const item of (header || '').split(',')) {
const i = item.indexOf('=');
if (i === -1) continue;
const key = item.slice(0, i), value = item.slice(i + 1);
if (key === 't' && t === undefined) t = value;
else if (key === 's') sigs.push(value);
}
if (!t || sigs.length === 0) return false;
const signed = Buffer.concat([rawBody, Buffer.from('_'), Buffer.from(t, 'latin1')]);
const expected = Buffer.from(
crypto.createHmac('sha256', secret).update(signed).digest('hex'));
return sigs.some((s) =>
/^[0-9a-fA-F]{64}$/.test(s) &&
crypto.timingSafeEqual(Buffer.from(s.toLowerCase()), expected));
}
Gotchas that cause most failures:
- Use the raw body (
express.raw({ type: '*/*' }),request.arrayBuffer(),await request.body()). NeverJSON.parse/JSON.stringifyfirst. - The body comes first, then
_, thent(nott.body). - Validate each
s(exactly 64 hex characters) before comparing in constant time. Hex is case-insensitive. - Missing header, missing
tors, malformed signature, or mismatch: return401. - No timestamp window by default. Hookdeck retries and replays resend the original headers and timestamp, so a fixed window would reject legitimate deliveries. The examples offer an opt-in
TIDIO_(WEBHOOK_ TOLERANCE_ SECONDS tis Unix seconds; unset or0= off).
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Envelope and Event Types
Every delivery is an HTTP POST with JSON { created_. Dispatch on topic; the event data is in content.
topic | Triggered When |
|---|---|
contact.created / contact.updated | A contact is created or updated |
conversation.solved_ | An operator marks a conversation as solved |
conversation.solved_ | Lyro (AI) considers the conversation finished (reason: handoff, taken_over_by_operator, inactive) |
conversation.solved_ | The Automatic Solve feature resolves a conversation |
conversation.operator_ | An operator message is persisted on the chat channel |
ticket.created / ticket.status_updated | A ticket is created or its status changes |
ticket.operator_assigned / ticket.holder_changed | A ticket gets a new operator or holder |
ticket.operator_replied / ticket.contact_replied / ticket.internal_ / ticket.replied | Replies and notes on a ticket (ticket.replied covers all of them) |
customer_ | A customer satisfaction survey is submitted |
Changes made through the Tidio OpenAPI do not trigger webhooks. See references/overview.md for payloads.
Delivery Behaviour
- Respond
2xxwithin 4 seconds; process asynchronously. - Non-2xx, timeout, or connection error: retried 9 times (5 s, 10 s, 30 s, 1 min, 3 min, 10 min, 30 min, 1 h, 3 h; about 4 h 45 min total).
- Ordering is not guaranteed (order by
created_at); duplicates are possible, so dedupe onwebhook_id.
Environment Variables
TIDIO_WEBHOOK_SECRET=your_tidio_webhook_secret # Settings > Developer > Webhooks
TIDIO_WEBHOOK_TOLERANCE_SECONDS=0 # optional opt-in replay window; 0 or unset = off
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 tidio --path /webhooks/tidio
Reference Materials
- references/overview.md - Tidio webhook concepts, envelope, and events
- references/setup.md - Configure webhooks in the Tidio panel
- references/verification.md - Signature verification details and gotchas