# Event catalog

Generated from `contracts/events.schema.json` by `node docs/gen.mjs`. Do not edit by hand.

Every event arrives as `{type, id, ts, data}`. `id` is a UUIDv7: deduplicate on it. Events are not ordered; see [ordering and limits](/realtime/ordering/). The tables below describe `data`.

| Type | Meaning |
|---|---|
| [`call.capture`](#callcapture) | Asks a peer to capture the other participant's video. Sent to the subject's peer. |
| [`call.ringing`](#callringing) | A call is ringing. Sent to the callee, with the caller as `caller`. |
| [`call.updated`](#callupdated) | A call changed. Sent to both participants. |
| [`conversation.updated`](#conversationupdated) | A conversation changed. Sent to both participants, each with their own summary. |
| [`delivered.updated`](#deliveredupdated) | Messages were delivered to a user. Sent to the other participant. |
| [`message.created`](#messagecreated) | A message was sent. Sent to both participants; not to one who blocked the sender. |
| [`message.updated`](#messageupdated) | A message changed. Sent to both participants; a dropped reaction goes to its author only. |
| [`photos.updated`](#photosupdated) | Photo consent or the released photos changed. Sent to both participants. |
| [`presence.changed`](#presencechanged) | A user's presence changed. Sent on their presence channel; `presence.fanout` adds peers. |
| [`purchase.updated`](#purchaseupdated) | A purchase changed. Sent to the buyer and the seller. |
| [`read.updated`](#readupdated) | A user read a message. Sent to the other participant while both have read receipts on. |
| [`session.revoked`](#sessionrevoked) | The user's session ended. Sent to that user. |
| [`typing`](#typing) | A user is typing. Sent to the other participant. |

## `call.capture`

Asks a peer to capture the other participant's video. Sent to the subject's peer.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `call_id` | uuid | required | Call to capture frames from. |
| `interval_seconds` | integer | required | Seconds between frames, from `moderation.calls.capture_interval_seconds`. |
| `max_frames` | integer | required | Most frames to capture, from `moderation.calls.capture_max_frames`. |
| `subject_id` | uuid | required | Participant whose video the peer captures. |

## `call.ringing`

A call is ringing. Sent to the callee, with the caller as `caller`.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `answered_at` | date-time or null | `null` | When the callee answered. |
| `callee_id` | uuid | required | The user who is called. |
| `caller` | [Peer](#peer-model) | required | The caller. |
| `caller_id` | uuid | required | The user who started the call. |
| `conversation_id` | uuid | required | The conversation the call belongs to. |
| `end_reason` | string or null | `null` | Why the call ended, for example `insufficient_funds`. |
| `ended_at` | date-time or null | `null` | When the call ended. |
| `id` | uuid | required | The call's id. |
| `started_at` | date-time | required | When the call was started. |
| `state` | `"ringing"`, `"active"`, `"ended"`, `"missed"`, `"declined"`, `"busy"` | required | `ringing` until answered, then `active`; `missed`, `declined` and `busy` end it unanswered, `ended` after it was answered. |
| `video` | boolean | `false` | How the callee's client rings; it changes nothing else. |

## `call.updated`

A call changed. Sent to both participants.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `answered_at` | date-time or null | `null` | When the callee answered. |
| `callee_id` | uuid | required | The user who is called. |
| `caller_id` | uuid | required | The user who started the call. |
| `conversation_id` | uuid | required | The conversation the call belongs to. |
| `end_reason` | string or null | `null` | Why the call ended, for example `insufficient_funds`. |
| `ended_at` | date-time or null | `null` | When the call ended. |
| `id` | uuid | required | The call's id. |
| `started_at` | date-time | required | When the call was started. |
| `state` | `"ringing"`, `"active"`, `"ended"`, `"missed"`, `"declined"`, `"busy"` | required | `ringing` until answered, then `active`; `missed`, `declined` and `busy` end it unanswered, `ended` after it was answered. |
| `video` | boolean | `false` | How the callee's client rings; it changes nothing else. |

## `conversation.updated`

A conversation changed. Sent to both participants, each with their own summary.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `alias` | string or null | `null` | The viewer's own name for the peer; null while `workspace.aliases` is off. |
| `archived` | boolean | `false` | Whether the viewer archived it; needs `workspace.queue`. |
| `id` | uuid | required | The conversation id. |
| `last_message` | [LastMessage](#lastmessage-model) or null | required | The latest message, or null. |
| `peer` | [Peer](#peer-model) | required | The other user. |
| `pinned` | boolean | `false` | Whether the viewer pinned it; false while `workspace.pins` is off. |
| `seq` | integer | required | The viewer's inbox sequence for this row; keep the higher `seq`. |
| `spent` | [Money](#money-model) or null | `null` | The peer's paid purchases from the viewer here. |
| `unread_count` | integer | required | Messages from the peer the viewer has not read. |
| `waiting_since` | date-time or null | `null` | Since when the peer waits on the viewer. |

## `delivered.updated`

Messages were delivered to a user. Sent to the other participant.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `conversation_id` | uuid | required | Conversation the messages belong to. |
| `last_delivered_message_id` | uuid | required | Last message delivered to that user. |
| `user_id` | uuid | required | User whose device received the messages. |

## `message.created`

A message was sent. Sent to both participants; not to one who blocked the sender.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `auto` | boolean | `false` | true for an away reply the server sent for its sender. |
| `caption` | string or null | `null` | The text under a media message. |
| `card` | [Card](#card-model) or null | `null` | The card of a `card` message. |
| `client_msg_id` | string or null | required | The id the sender chose when sending, which dedupes retries, or null. |
| `conversation_id` | uuid | required | The conversation it belongs to. |
| `created_at` | date-time | required | When it was sent. |
| `deleted` | boolean | `false` | true when the sender deleted it for everyone. It has no text, caption, photo, card or quote. |
| `delivered_to_peer` | boolean | `false` | true for the viewer's own message once the peer received or read it, never for a silently dropped one. |
| `duration_ms` | integer or null | `null` | The length of a video or voice note in milliseconds. |
| `edited_at` | date-time or null | `null` | When the sender last edited it. |
| `forwarded` | boolean | `false` | true when the sender marked it forwarded, or it came from the forward route. |
| `height` | integer or null | `null` | The media's display height in pixels. |
| `id` | uuid | required | The message's id. |
| `kind` | `"text"`, `"photo"`, `"video"`, `"voice"`, `"system"`, `"card"` | required | `text`, `photo`, `video`, `voice`, `system` or `card`. |
| `once` | boolean | `false` | true for view-once media: it carries no links and no caption for anyone, and its recipient opens it through the open route. |
| `opened_at` | date-time or null | `null` | When the recipient first opened a view-once message or played a voice note. The sender sees null on view once while either participant has `read_receipts` off. |
| `photo` | [Photo](#photo-model) or null | `null` | The media of a photo, video or voice message. |
| `reactions` | list of [Reaction](#reaction-model) | `[]` | One entry per emoji in the order they were set; none on a deleted or redacted message. |
| `read_by_peer` | boolean | required | true for the viewer's own message once the peer read it; false while either participant has `read_receipts` off. |
| `redacted` | boolean | required | true when a moderator removed the content. |
| `reply_to` | [ReplyTo](#replyto-model) or null | `null` | The quoted message, or null once it is deleted, redacted or hidden from you. |
| `sender_id` | uuid or null | required | The sender's id, null for a system message. |
| `seq` | integer | required | The message's place in its conversation's sync order. Keep the copy with the higher `seq`. |
| `status` | `"sent"`, `"held"`, `"rejected"`, `"undelivered"` | required | `held` and `rejected` come from moderation or photo consent, and `undelivered` is a message dropped silently because the peer blocked the sender. |
| `text` | string or null | `null` | The text. A voice note's text is its preview, `🎤 m:ss`. A deleted message has none. |
| `wave` | string or null | `null` | A voice note's level bars. |
| `width` | integer or null | `null` | The media's display width in pixels. |

## `message.updated`

A message changed. Sent to both participants; a dropped reaction goes to its author only.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `auto` | boolean | `false` | true for an away reply the server sent for its sender. |
| `caption` | string or null | `null` | The text under a media message. |
| `card` | [Card](#card-model) or null | `null` | The card of a `card` message. |
| `client_msg_id` | string or null | required | The id the sender chose when sending, which dedupes retries, or null. |
| `conversation_id` | uuid | required | The conversation it belongs to. |
| `created_at` | date-time | required | When it was sent. |
| `deleted` | boolean | `false` | true when the sender deleted it for everyone. It has no text, caption, photo, card or quote. |
| `delivered_to_peer` | boolean | `false` | true for the viewer's own message once the peer received or read it, never for a silently dropped one. |
| `duration_ms` | integer or null | `null` | The length of a video or voice note in milliseconds. |
| `edited_at` | date-time or null | `null` | When the sender last edited it. |
| `forwarded` | boolean | `false` | true when the sender marked it forwarded, or it came from the forward route. |
| `height` | integer or null | `null` | The media's display height in pixels. |
| `id` | uuid | required | The message's id. |
| `kind` | `"text"`, `"photo"`, `"video"`, `"voice"`, `"system"`, `"card"` | required | `text`, `photo`, `video`, `voice`, `system` or `card`. |
| `once` | boolean | `false` | true for view-once media: it carries no links and no caption for anyone, and its recipient opens it through the open route. |
| `opened_at` | date-time or null | `null` | When the recipient first opened a view-once message or played a voice note. The sender sees null on view once while either participant has `read_receipts` off. |
| `photo` | [Photo](#photo-model) or null | `null` | The media of a photo, video or voice message. |
| `reactions` | list of [Reaction](#reaction-model) | `[]` | One entry per emoji in the order they were set; none on a deleted or redacted message. |
| `read_by_peer` | boolean | required | true for the viewer's own message once the peer read it; false while either participant has `read_receipts` off. |
| `redacted` | boolean | required | true when a moderator removed the content. |
| `reply_to` | [ReplyTo](#replyto-model) or null | `null` | The quoted message, or null once it is deleted, redacted or hidden from you. |
| `sender_id` | uuid or null | required | The sender's id, null for a system message. |
| `seq` | integer | required | The message's place in its conversation's sync order. Keep the copy with the higher `seq`. |
| `status` | `"sent"`, `"held"`, `"rejected"`, `"undelivered"` | required | `held` and `rejected` come from moderation or photo consent, and `undelivered` is a message dropped silently because the peer blocked the sender. |
| `text` | string or null | `null` | The text. A voice note's text is its preview, `🎤 m:ss`. A deleted message has none. |
| `wave` | string or null | `null` | A voice note's level bars. |
| `width` | integer or null | `null` | The media's display width in pixels. |

## `photos.updated`

Photo consent or the released photos changed. Sent to both participants.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `consent` | `"yes"`, `"no"` or null | required | The receiver's photo consent for the conversation; null until they answer. |
| `conversation_id` | uuid | required | Conversation whose photos changed. |
| `rejected_ids` | list of uuid | required | Photos rejected and deleted from storage. |
| `released_ids` | list of uuid | required | Photos released to the receiver. |

## `presence.changed`

A user's presence changed. Sent on their presence channel; `presence.fanout` adds peers.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `last_seen_at` | date-time or null | required | When the user was last seen. Null when the user has `last_seen` set to nobody or `presence.show_last_seen` is off. |
| `online` | boolean | required | Whether the user is online now. |
| `user_id` | uuid | required | User whose presence changed. |

## `purchase.updated`

A purchase changed. Sent to the buyer and the seller.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `buyer_id` | uuid | required | The buyer's id. |
| `conversation_id` | uuid or null | required | The conversation it belongs to, or null. |
| `created_at` | date-time | required | When the purchase was created. |
| `currency` | string | required | ISO 4217 currency of the price. |
| `external_ref` | string or null | `null` | Your reference for the purchase, such as an order id. |
| `id` | uuid | required | The purchase's id. |
| `item` | [PurchaseItem](#purchaseitem-model) | required | The item that was bought. |
| `mode` | `"server_charge"`, `"page_callback"`, `"checkout_url"`, `"external"` | required | How it is paid: the app's `commerce.payment_mode`, or `external` for a purchase recorded from your server. |
| `price_minor` | integer | required | The price in minor units. |
| `seller_id` | uuid | required | The seller's id. |
| `status` | `"pending"`, `"paid"`, `"insufficient"`, `"failed"`, `"cancelled"` | required | `pending` until paid; `insufficient` when the balance was short. |

## `read.updated`

A user read a message. Sent to the other participant while both have read receipts on.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `conversation_id` | uuid | required | Conversation that was read. |
| `last_read_message_id` | uuid | required | Last message the reader has read. |
| `reader_id` | uuid | required | User who read it. |

## `session.revoked`

The user's session ended. Sent to that user.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `reason` | `"banned"`, `"deleted"`, `"merged"` | required | Why the session ended. |
| `temporary` | boolean | `false` | True for a ban that expires. |

## `typing`

A user is typing. Sent to the other participant.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `conversation_id` | uuid | required | Conversation the user is typing in. |
| `user_id` | uuid | required | User who is typing. |

## Shared models

### Card model

A structured message such as an offer, a purchase or a call event.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `data` | map of key to any | `{}` | The card's content; its shape depends on `type`. |
| `type` | `"call_invite"`, `"call_ended"`, `"call_missed"`, `"purchase"`, `"offer"`, `"system"`, `"signup_invite"` | required | What the card is. |

### LastMessage model

The latest message of a conversation, as the viewer sees it.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `auto` | boolean | `false` | true for an away reply. |
| `card_type` | `"call_invite"`, `"call_ended"`, `"call_missed"`, `"purchase"`, `"offer"`, `"system"`, `"signup_invite"` or null | `null` | The card's type for a card, else null. |
| `created_at` | date-time | required | When the message was sent. |
| `deleted` | boolean | `false` | true when deleted for everyone. |
| `delivered_to_peer` | boolean | `false` | true only for the viewer's own message once the peer received or read it. |
| `duration_ms` | integer or null | `null` | A voice note's length or an ended call's length. |
| `id` | uuid | required | The message id. |
| `kind` | `"text"`, `"photo"`, `"video"`, `"voice"`, `"system"`, `"card"` | required | `text`, `photo`, `video`, `voice`, `system` or `card`. |
| `once` | boolean | `false` | true for a view-once message. |
| `preview` | string | required | The text or caption up to 140 characters; empty for a message deleted for everyone; `removed by moderation` for a redacted one. |
| `read_by_peer` | boolean | `false` | true only for the viewer's own message once the peer has read it, and false while either participant has `read_receipts` off. |
| `sender_id` | uuid or null | required | The sender's user id; null for a system message. |

### Money model

An amount of money.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `amount_minor` | integer | required | The amount in minor units, such as cents. |
| `currency` | string | required | ISO 4217 currency code. |

### Peer model

The other user of a conversation.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `avatar` | string or null | `null` | https URL of the picture, or null. |
| `external_id` | string | required | The user's id on your site. |
| `id` | uuid | required | The user's id in mondochat. |
| `last_seen_at` | date-time or null | `null` | Null when the viewer or the peer has `last_seen: nobody` or `presence.show_last_seen` is off. |
| `name` | string | required | Display name. |
| `online` | boolean or null | `null` | Present only when the viewer may see presence (`presence.visible`). |
| `presence_pass` | string or null | `null` | Authorizes a subscribe to `/presence/{id}` for 1 hour; present with the presence fields. |
| `role` | string | required | A role key from the app's `roles`. |
| `tier` | string or null | `null` | A key of workspace.tiers, else null. |

### Photo model

A photo, video, voice note or avatar and the links to read it.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `full_url` | string or null | required | Signed link to the full rendition, or to a video's MP4, which lives 1 hour. Null for view-once media and voice notes, which you open through the open route. |
| `height` | integer or null | required | Display height in pixels, or null. |
| `id` | uuid | required | The media's id. |
| `kind` | `"photo"`, `"video"`, `"voice"`, `"avatar"` | required | What the media is. |
| `status` | `"pending"`, `"ready"`, `"held"`, `"rejected"`, `"deleted"` | required | `pending` until the upload is completed, `held` while it waits for the receiver's consent or for moderation, `rejected` or `deleted` once it is gone. |
| `thumb_url` | string or null | required | Signed link to the 600 px thumbnail, or to a video's poster. It lives 5 to 10 minutes; fetch the media again when it fails. Null for view-once media and voice notes. |
| `uploader_id` | uuid | required | The id of the user who uploaded it. |
| `width` | integer or null | required | Display width in pixels, or null. |

### PurchaseItem model

What was bought.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `description` | string, up to 2000 chars or null | `null` | More about it. |
| `id` | string or null | `null` | The catalog item's id, when there is one. |
| `kind` | string or null | `null` | The item's kind; `custom` for a custom amount. |
| `title` | string, up to 200 chars | required | The item's name. |

### Reaction model

One emoji on a message and how many users set it.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `count` | integer | required | How many users reacted with it. |
| `emoji` | string | required | The emoji. |
| `mine` | boolean | required | true when the viewer's own reaction is this emoji. |

### ReplyTo model

The message a message quotes.

| Field | Type | Default | Meaning |
|---|---|---|---|
| `id` | uuid | required | The quoted message's id. |
| `kind` | `"text"`, `"photo"`, `"video"`, `"voice"`, `"system"`, `"card"` | required | The quoted message's kind. |
| `preview` | string | required | The quoted text or caption, up to 140 characters; empty for a view-once message or a card. |
| `sender_id` | uuid or null | required | The quoted message's sender, null for a system message. |