Events catalog
Every event opendesk emits, its visibility and its data.
Every state change that someone else should see is written as an event, in the same database transaction as the change. Events are delivered three ways: on the realtime gateway, to webhooks, and through replay with GET /v1/events?since=<seq>.
How events flow
Section titled “How events flow”- A request writes its change and the event in one transaction, so an event exists exactly when its change does.
- The relay reads new events in
seqorder and publishes each to NATS onod.events.<workspace_id>. It also queues a signed delivery for each matching webhook. - The realtime gateway pushes each event to the topics it names. Most events go to
workspace:<wid>, which everyone in the workspace joins. - An event about a ticket in a private team goes in full to
team:<wid>:<tid>, which only people who can see that team’s tickets can join.workspace:<wid>gets a stand-in with the sameseqand no data (type: "event.hidden"), soseqstays contiguous for everyone. Replay answers the same stand-in for events you may not see, and a webhook receives them only when it was granted that team.
Envelope
Section titled “Envelope”{ "id": "0192f0c4-...", // UUID v7, unique "seq": 1042, // contiguous per workspace "workspace_id": "…", "type": "message.created", "visibility": "agents", // "agents" | "public" "topics": ["workspace:<wid>", "user:<uid>", "contact:<wid>:<contact_id>"], "data": { }, "occurred_at": "2026-10-11T09:00:00Z"}seqincreases by exactly one per event within a workspace. A jump means you missed events.visibility: "public"events may reach the end customer in the chat widget. Everything else is"agents": internal notes, assignment, tags, SLA and all settings.topicsdecide realtime fan-out. Webhooks receive the envelope withouttopics.- Objects in
dataare the same JSON the REST API returns.
Catalog
Section titled “Catalog”| Type | Visibility | Extra topics | Data |
|---|---|---|---|
ticket.created |
agents | { ticket } |
|
ticket.updated |
agents | user:<assignee> when the assignee changes |
{ ticket, changes: ["status", "assignee_id", "fields", ...] } |
ticket.deleted |
agents | { ticket_id } |
|
message.created |
see below | contact:<wid>:<cid> when public |
{ ticket_id, message } |
message.updated |
agents | { ticket_id, message }, for delivery status changes |
|
activity.created |
agents | { ticket_id, activity } |
|
contact.created, contact.updated |
agents | { contact } |
|
contact.deleted |
agents | { contact_id } |
|
company.created, company.updated |
agents | { company } |
|
company.deleted |
agents | { company_id } |
|
tag.created, tag.updated |
agents | { tag } |
|
tag.deleted |
agents | { tag_id } |
|
tag.merged |
agents | { tag_id, into } (records that carried it change without their own events; refetch lists) |
|
tag_group.created, tag_group.updated |
agents | { tag_group } |
|
tag_group.deleted |
agents | { tag_group_id } |
|
custom_field.created, custom_field.updated |
agents | { custom_field } |
|
custom_field.deleted |
agents | { custom_field_id, entity, key } |
|
mailbox.created, mailbox.updated |
agents | { mailbox } |
|
mailbox.deleted |
agents | { mailbox_id } |
|
member.added, member.updated |
agents | user:<uid> |
{ member }, also on a picture, availability or cap change |
member.removed |
agents | user:<uid> |
{ user_id } |
workspace.updated |
agents | { workspace }, when the name, settings, logo or a brand image changes |
|
notification.created |
agents | user:<uid> |
{ notification } |
saved_reply.*, view.*, automation.*, sla_policy.*, kb_article.*, kb_collection.*, team.*, webhook.* |
agents | { <resource> } on create and update, { <resource>_id } on delete |
|
billing.updated |
agents | Billing, see Billing API. Cloud only. |
|
webhook.test |
sent only to the webhook being tested |
message.created visibility
Section titled “message.created visibility”| Message | Visibility |
|---|---|
| A note | agents |
| A reply on a chat ticket | public, also sent to the contact’s topic |
| A reply on an email ticket | agents (the customer receives it by email) |
Gateway-only events
Section titled “Gateway-only events”chat.typing is not stored, has no seq and never reaches webhooks. The gateway sends typing indicators directly to the ticket and contact topics. See Realtime.
AI summaries and triage results arrive as ticket.updated with custom in changes (Ollie’s decisions in custom.ai_decisions, pending suggestions in custom.ai_suggestions); fields triage applied are listed too, such as priority, severity and tags. Chat widget AI answers arrive as a normal message.created.
Retention
Section titled “Retention”Events are kept for 7 days (EVENT_RETENTION on self-hosted instances) for replay, then pruned.