Skip to main content
The resource’s own fields, with an event key merged in at the top level. That means you parse a webhook body differently from a list response elsewhere in this API, which does wrap its payload. event is always present and always matches one of the 12 names in the event catalogue.

Conversation events

conversation_created, conversation_updated, and conversation_status_changed all share the same conversation shape. updated and status_changed additionally carry changed_attributes.
Conversation payload
id here is the conversation’s display ID — the same number used in /conversations/{id} path params, not an internal primary key you’d need to translate. conversation_updated fires for any attribute change; conversation_status_changed fires specifically when status changes, and always carries a status entry in changed_attributes. conversation_created has no changed_attributes key at all.

Message events

message_created and message_updated — subject to the incoming/outgoing/template gate — carry the message plus its account, inbox, and full conversation nested inside:
Message payload
attachments is only present at all if the message has any — it’s an array of attachment objects, omitted entirely rather than sent empty.

Contact and inbox events

contact_created, contact_updated, inbox_created, and inbox_updated follow the same pattern as each other: the resource’s own fields, flattened, plus changed_attributes on the _updated variant.
contact_updated and inbox_updated don’t fire on a no-op update. If the update request didn’t actually change any tracked attribute, there’s no webhook — the listener checks for a non-empty changed_attributes before dispatching at all. Don’t rely on an update webhook as proof a PATCH request was received; rely on it as proof something changed.
Contact payload
Note the field name difference: the contact payload’s avatar field is avatar, not thumbnail — the two other resources here (conversations, contact inboxes) use thumbnail. This isn’t a typo you should normalize away; it’s just how each object’s own webhook_data method names it. Inbox payloads carry configuration, not just identity — greeting_message, working_hours, auto_assignment_config, csat_survey_enabled, and the associated channel object, alongside created_at/updated_at.

webwidget_triggered

Carries the contact_inbox context plus whatever event_info the widget attached:
Web widget payload
current_conversation is null until this contact_inbox has an actual conversation — expect it on a returning visitor, not a first pageview.

Typing events

conversation_typing_on and conversation_typing_off are the one pair that doesn’t go through a webhook_data merge — they’re a small, purpose-built object:
Typing payload

Next

Signature verification

Before you trust any of this: what actually proves it came from Tied.

Retries and testing

What happens when your endpoint doesn’t respond in time.