{
  "openapi": "3.1.0",
  "info": {
    "title": "Jelly API reference",
    "version": "2026-10-22",
    "description": "The Jelly API lets a program read and change a team's email over HTTP.\nEvery endpoint is under `/api`. This reference is generated from the\ncode, so it lists every endpoint. It is also available as\n[OpenAPI 3.1](/api-reference/openapi.json) and as\n[markdown](/api-reference/reference.md).\n\nHow tokens work, and what a token can see and change, is in the manual:\n[The API](/manual/advanced/api).\n\n## Tokens\n\nSend a token in the `Authorization` header:\n\n```\nAuthorization: Bearer jelly_…\n```\n\nThere are two kinds of token:\n\n- **Team token** (`jelly_…`): made by an Admin on **Settings → API\n  tokens**. It acts in its own name and sees only the mailboxes\n  it was given. It cannot send email.\n- **Personal token** (`jellyp_…`): a person's own sign-in, from an app\n  that uses [OAuth](/manual/advanced/api#oauth): Jelly for iOS, or an\n  app the team registered. It can do what that person can do in the web\n  app, including sending, within its [scopes](#description/scopes). It\n  lasts an hour.\n\nEach endpoint shows which tokens it accepts. Endpoints about one\nperson's own view of the mail, and sending, need a personal token. A\nteam token gets `403 needs_person` there. Changing labels or the\nautoresponder needs **change team settings**.\n\n## Scopes\n\nA personal token from an app the team registered (see\n[Apps](/manual/advanced/api#apps)) carries scopes. Each endpoint shows\nthe scope it needs:\n\n| Scope | What it allows |\n| --- | --- |\n| `read` | Every `GET`: conversations, threads, notes, contacts, people, the live stream |\n| `write` | Every change that sends nothing and is not a team setting: actions, notes, drafts, fields, contacts |\n| `send` | Sending a draft, and undoing a send |\n| `settings` | Changing labels, the autoresponder, field definitions and contact card sources |\n\n`GET /api/me` needs no scope. A request outside the token's scopes gets\n`403 insufficient_scope`, with the scope it needed in `scope` and in\n`WWW-Authenticate`:\n\n```\nWWW-Authenticate: Bearer realm=\"jelly\", error=\"insufficient_scope\", scope=\"send\"\n```\n\nA scope never gives a person more than they have. A Member's token with\n`settings` still gets `403 forbidden` when it changes a label, because\nonly an Admin may. Personal tokens from Jelly's own apps have no scopes\nand can do what the person can do.\n\nSome links need no token, because they are signed: file downloads,\npictures in messages, and avatars. These are marked **Signed link**.\n\n## Versions\n\nThe API has dated versions. Send the version your client was written\nfor in the `Jelly-Version` header:\n\n```\nJelly-Version: 2026-10-22\n```\n\nEvery answer has a `Jelly-Version` header naming the version it was\nanswered as.\n\nWithout the header, Jelly uses the version the token is pinned to. A\nteam token, and an app the team registers, is pinned to the newest\nversion on the day it is made. Tokens made before 22 October 2026, and\nJelly's own apps, get the oldest version still supported.\n\nA date that is not a supported version gets `400 unknown_version`, with\nthe oldest and newest supported dates in `oldest` and `newest`.\n\nThe supported versions are `2026-10-09` and `2026-10-22`. What changed\nbetween them is in the [Changelog](#description/changelog).\n\n## What we promise\n\n- Additive changes can happen at any time: new endpoints, new fields\n  in an answer, new webhook events, and new values for fields such as\n  `status`. Your client must ignore fields and values it does not know.\n- Fields in a request that Jelly does not know are ignored.\n- A breaking change, such as removing or renaming a field, changing\n  what a field means, or refusing a request that used to work, comes\n  only with a new version. Your version does not change until you\n  change it.\n- A version is supported for 12 months after the next one is released.\n  An answer on an older version has a `Deprecation` header, the date\n  the next version was released, and a `Sunset` header, the date the\n  older one stops being supported. After that date, a token pinned to\n  it gets the oldest version still supported.\n- Every change is in the [Changelog](#description/changelog), with its\n  date and the endpoints it affects.\n\n### Tiers\n\nEach endpoint has a tier:\n\n- **Integration**: endpoints a team token can call. The promise above\n  applies in full.\n- **App**: endpoints only a personal token can call. They show one\n  person's view of the mail as Jelly's own apps draw it: inbox sections\n  and their titles, highlighted search matches, activity lines. They\n  follow the same versions, but section titles, colours and the\n  wording of lines follow the apps and can change in any version.\n\n## Identifiers\n\n| Resource | Id |\n| --- | --- |\n| Conversation | its slug, such as `broken-widget-k3f9zq` |\n| Message, note | its key |\n| Label, contact | its slug |\n| Member, mailbox, attachment, draft | a number |\n\nA label's id stays the same when the label is renamed.\n\n## Responses\n\nA single resource is returned under its name, such as\n`{ \"conversation\": {…} }`. A list is returned under its plural, with a\ncursor:\n\n```json\n{ \"conversations\": [ … ], \"next_cursor\": \"…\" }\n```\n\nTo get the next page, send `next_cursor` back as `?cursor=`. It is\n`null` on the last page. Some lists accept `?limit=` from 1 to 100; the\ndefault is 50. Short lists, such as labels and members, are returned in\nfull.\n\n### People\n\nWhere a response says who did something, it is one of:\n\n```json\n{ \"type\": \"member\",    \"id\": 3, \"name\": \"Sarah Chen\", \"email\": \"sarah@acme.test\", \"role\": \"admin\", \"active\": true, \"avatar\": { \"…\": \"…\" } }\n{ \"type\": \"api_token\", \"id\": 7, \"name\": \"The night shift\" }\nnull\n```\n\n`null` means Jelly did it, for example when a rule ran. A member who\nacted through an assistant, such as Claude, or through an app the team\nregistered, also has `via`, naming the assistant or the app.\n\n### Avatars\n\nA member, contact or sender has an `avatar`:\n\n```json\n\"avatar\": {\n  \"url\": \"https://jelly.acme.test/api/avatars/3f9c…/download?expires=…&signature=…\",\n  \"initials\": \"SC\",\n  \"tint\": {\n    \"light\": { \"background\": \"#5e35b1\", \"text\": \"#ffffff\" },\n    \"dark\":  { \"background\": \"#5e35b1\", \"text\": \"#ffffff\" }\n  }\n}\n```\n\nWhen `url` is `null`, show `initials` on the `tint` colour, as the web\napp does. `url` is a signed link that lasts at least a day. Cache the\npicture by the `{hash}` in its path, since the rest of the link changes.\n\n### Links\n\nConversations, messages and notes have a `url`: their page in the web\napp. Use it as given rather than building links yourself.\n\n## Errors\n\nAn error has an `error` code and a `message` you can show to a person:\n\n```json\n{ \"error\": \"not_found\", \"message\": \"No such resource.\" }\n```\n\n| Status | Meaning |\n| --- | --- |\n| 400 | `unknown_version`: the `Jelly-Version` header names no supported version |\n| 422 | The request is not valid. `errors` lists the problems |\n| 401 | No token, or the token does not work. An expired personal token also gets `WWW-Authenticate: Bearer error=\"invalid_token\"`: refresh it |\n| 402 | `paused`: the team's subscription is paused. Team tokens are refused; personal tokens can only read |\n| 403 | `needs_person`: this needs a personal token. `forbidden`: this token or person may not do this. `insufficient_scope`: the token's scopes do not include `scope` |\n| 404 | Not found, or not visible to this token |\n| 405 | Wrong method for this path |\n| 409 | Conflicts with the current state, such as a draft that changed (`stale`), or a retry while the first request is still running (`idempotency_in_progress`) |\n| 410 | A signed link has expired, or `cursor_expired`: a change feed cursor is too old |\n| 413 | Files over 10 MB |\n| 415 | Not a picture |\n| 423 | `locked`: someone else is editing this draft |\n| 429 | Too many requests. `Retry-After` says how long to wait |\n| 500 | Server error. `ref` identifies it in the server's log |\n\nA `422` lists each problem in `errors`, with the field it is about:\n\n```json\n{\n  \"error\": \"unprocessable\",\n  \"message\": \"The request could not be accepted.\",\n  \"errors\": [\n    { \"field\": \"until\", \"code\": \"invalid\", \"message\": \"until must be in the future\" }\n  ]\n}\n```\n\n`field` is `null` when the problem is not about one field. `code` is\n`required`, `unknown` (the value names nothing this token can use) or\n`invalid`. More codes may be added. Before version `2026-10-22`, each\nproblem is its `message` alone, as a string.\n\n## Rate limits\n\n- 5,000 requests every 5 minutes from one address.\n- 100,000 requests a day for the whole team, reset at midnight UTC.\n\nBoth return `429` with `Retry-After` when reached.\n\n## Retrying safely\n\nSend an `Idempotency-Key` header on a `POST`, `PATCH` or `DELETE` to\nmake it safe to send again. Use a new random value, such as a UUID, for\neach new request, and the same value when you retry that request. The\nkey is 1 to 255 characters.\n\n- The first request runs, and Jelly keeps its answer for 24 hours.\n- A retry with the same key, method, path and body does not run again.\n  It gets the first answer back, status and body, with\n  `Idempotent-Replayed: true`. This is true of a refusal too: a `422`\n  is sent again as a `422`.\n- A server error (`500`) is not kept. A retry runs again.\n- The same key with a different method, path or body is refused with\n  `422` and `idempotency_key_reused`.\n- A retry while the first request is still running is refused with\n  `409`, `idempotency_in_progress` and `Retry-After`. Wait and send it\n  again.\n\nKeys belong to the token that sent them, and a refreshed personal token\nkeeps the keys of the sign-in it came from. Two tokens can use the same\nkey without meeting. A request without the header is not remembered.\n\n`client_key` on `POST /api/drafts` and\n`POST /api/conversations/{id}/draft_reply` still works. The header is\nthe general form, and works for team tokens too.\n\nEvery response says where you stand against whichever of the two is\ncloser to running out:\n\n| Header | Meaning |\n| --- | --- |\n| `X-RateLimit-Limit` | The size of that limit: 5,000 or 100,000 |\n| `X-RateLimit-Remaining` | Requests left before it is reached |\n| `X-RateLimit-Reset` | When it starts again, in Unix seconds |\n\nTo keep up with changes without polling every list, use\n[`GET /api/changes`](#tag/changes).\n\n## Capabilities\n\n`GET /api/me` lists `capabilities`, what this server can do that\nanother may not. Today that is `push` alone. Every other feature in this\nreference is on every server.\n\n## Every endpoint\n\nEach endpoint, the tokens it accepts, and its tier.\n\n| Endpoint | What it does | Accepts | Tier |\n|---|---|---|---|\n| [<code>GET <span>/api</span><span>/me</span></code>](#tag/me/GET/api/me) | Get the signed-in person and server capabilities | Personal | App |\n| [<code>PATCH <span>/api</span><span>/me</span><span>/preferences</span></code>](#tag/me/PATCH/api/me/preferences) | Update the signed-in person's preferences | Personal | App |\n| [<code>GET <span>/api</span><span>/app</span></code>](#tag/apps/GET/api/app) | Get the installation of the app this token belongs to | Personal | App |\n| [<code>GET <span>/api</span><span>/inbox</span></code>](#tag/inbox-and-lists/GET/api/inbox) | Get the inbox | Personal | App |\n| [<code>GET <span>/api</span><span>/lists</span><span>/{list}</span></code>](#tag/inbox-and-lists/GET/api/lists/{list}) | Get a list | Personal | App |\n| [<code>GET <span>/api</span><span>/sidebar</span></code>](#tag/inbox-and-lists/GET/api/sidebar) | Get counts, mailboxes and labels | Personal | App |\n| [<code>GET <span>/api</span><span>/conversations</span></code>](#tag/conversations/GET/api/conversations) | List conversations | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/conversations</span><span>/search</span></code>](#tag/conversations/GET/api/conversations/search) | Search conversations | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/conversations</span><span>/{id}</span></code>](#tag/conversations/GET/api/conversations/{id}) | Get a conversation | Team, personal | Integration |\n| [<code>PATCH <span>/api</span><span>/conversations</span><span>/{id}</span></code>](#tag/conversations/PATCH/api/conversations/{id}) | Update a conversation's fields and external id | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/conversations</span><span>/{id}</span><span>/thread</span></code>](#tag/conversations/GET/api/conversations/{id}/thread) | Get a conversation's thread | Personal | App |\n| [<code>GET <span>/api</span><span>/conversations</span><span>/{id}</span><span>/markdown</span></code>](#tag/conversations/GET/api/conversations/{id}/markdown) | Get a conversation as markdown | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/conversations</span><span>/{id}</span><span>/messages</span></code>](#tag/conversations/GET/api/conversations/{id}/messages) | List a conversation's messages | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/bulk</span></code>](#tag/conversations/POST/api/conversations/bulk) | Change many conversations at once | Personal | App |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/archive</span></code>](#tag/conversation-actions/POST/api/conversations/{id}/archive) | Archive | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/archive</span></code>](#tag/conversation-actions/DELETE/api/conversations/{id}/archive) | Unarchive | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/trash</span></code>](#tag/conversation-actions/POST/api/conversations/{id}/trash) | Move to the Trash | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/trash</span></code>](#tag/conversation-actions/DELETE/api/conversations/{id}/trash) | Restore from the Trash | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/spam</span></code>](#tag/conversation-actions/POST/api/conversations/{id}/spam) | Mark as spam | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/spam</span></code>](#tag/conversation-actions/DELETE/api/conversations/{id}/spam) | Mark as not spam | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/snooze</span></code>](#tag/conversation-actions/POST/api/conversations/{id}/snooze) | Snooze | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/snooze</span></code>](#tag/conversation-actions/DELETE/api/conversations/{id}/snooze) | End a snooze | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/labels</span></code>](#tag/conversation-actions/POST/api/conversations/{id}/labels) | Add a label | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/labels</span><span>/{label}</span></code>](#tag/conversation-actions/DELETE/api/conversations/{id}/labels/{label}) | Remove a label | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/assignments</span></code>](#tag/conversation-actions/POST/api/conversations/{id}/assignments) | Assign | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/assignments</span><span>/{member}</span></code>](#tag/conversation-actions/DELETE/api/conversations/{id}/assignments/{member}) | Unassign | Team, personal | Integration |\n| [<code>PATCH <span>/api</span><span>/conversations</span><span>/{id}</span><span>/mailboxes</span></code>](#tag/conversation-actions/PATCH/api/conversations/{id}/mailboxes) | Set mailboxes | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/read</span></code>](#tag/your-own-marks/POST/api/conversations/{id}/read) | Mark read | Personal | App |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/read</span></code>](#tag/your-own-marks/DELETE/api/conversations/{id}/read) | Mark unread | Personal | App |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/bookmark</span></code>](#tag/your-own-marks/POST/api/conversations/{id}/bookmark) | Bookmark | Personal | App |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/bookmark</span></code>](#tag/your-own-marks/DELETE/api/conversations/{id}/bookmark) | Remove bookmark | Personal | App |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/follow</span></code>](#tag/your-own-marks/POST/api/conversations/{id}/follow) | Follow | Personal | App |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/follow</span></code>](#tag/your-own-marks/DELETE/api/conversations/{id}/follow) | Stop following | Personal | App |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/aside</span></code>](#tag/your-own-marks/POST/api/conversations/{id}/aside) | Set aside | Personal | App |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/aside</span></code>](#tag/your-own-marks/DELETE/api/conversations/{id}/aside) | Undo set aside | Personal | App |\n| [<code>GET <span>/api</span><span>/conversations</span><span>/{id}</span><span>/notes</span></code>](#tag/notes/GET/api/conversations/{id}/notes) | List notes | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/notes</span></code>](#tag/notes/POST/api/conversations/{id}/notes) | Add a note | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/notes</span><span>/{note}</span></code>](#tag/notes/DELETE/api/conversations/{id}/notes/{note}) | Delete a note | Personal | App |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/notes</span><span>/{note}</span><span>/reactions</span></code>](#tag/notes/POST/api/conversations/{id}/notes/{note}/reactions) | Add a reaction | Personal | App |\n| [<code>PATCH <span>/api</span><span>/conversations</span><span>/{id}</span><span>/notes</span><span>/{note}</span><span>/reactions</span><span>/{reaction}</span></code>](#tag/notes/PATCH/api/conversations/{id}/notes/{note}/reactions/{reaction}) | Change a reaction | Personal | App |\n| [<code>DELETE <span>/api</span><span>/conversations</span><span>/{id}</span><span>/notes</span><span>/{note}</span><span>/reactions</span><span>/{reaction}</span></code>](#tag/notes/DELETE/api/conversations/{id}/notes/{note}/reactions/{reaction}) | Remove a reaction | Personal | App |\n| [<code>GET <span>/api</span><span>/reactions</span></code>](#tag/notes/GET/api/reactions) | List reaction emoji | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/messages</span><span>/{key}</span></code>](#tag/messages/GET/api/messages/{key}) | Get a message | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/messages</span></code>](#tag/messages/POST/api/messages) | Insert a message | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/draft_reply</span></code>](#tag/drafts/POST/api/conversations/{id}/draft_reply) | Start a reply | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/drafts</span></code>](#tag/drafts/POST/api/drafts) | Start a new message or forward | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/drafts</span><span>/{id}</span></code>](#tag/drafts/GET/api/drafts/{id}) | Get a draft | Team, personal | Integration |\n| [<code>PATCH <span>/api</span><span>/drafts</span><span>/{id}</span></code>](#tag/drafts/PATCH/api/drafts/{id}) | Save a draft | Team, personal | Integration |\n| [<code>DELETE <span>/api</span><span>/drafts</span><span>/{id}</span></code>](#tag/drafts/DELETE/api/drafts/{id}) | Discard a draft | Personal | App |\n| [<code>POST <span>/api</span><span>/drafts</span><span>/{id}</span><span>/heartbeat</span></code>](#tag/drafts/POST/api/drafts/{id}/heartbeat) | Keep holding a draft | Personal | App |\n| [<code>DELETE <span>/api</span><span>/drafts</span><span>/{id}</span><span>/lock</span></code>](#tag/drafts/DELETE/api/drafts/{id}/lock) | Release a draft | Personal | App |\n| [<code>POST <span>/api</span><span>/drafts</span><span>/{id}</span><span>/attachments</span></code>](#tag/drafts/POST/api/drafts/{id}/attachments) | Add a file to a draft | Personal | App |\n| [<code>DELETE <span>/api</span><span>/drafts</span><span>/{id}</span><span>/attachments</span><span>/{attachment}</span></code>](#tag/drafts/DELETE/api/drafts/{id}/attachments/{attachment}) | Remove a file from a draft | Personal | App |\n| [<code>POST <span>/api</span><span>/drafts</span><span>/{id}</span><span>/saved_reply_files</span></code>](#tag/drafts/POST/api/drafts/{id}/saved_reply_files) | Copy a saved reply's files onto a draft | Personal | App |\n| [<code>POST <span>/api</span><span>/uploads</span></code>](#tag/drafts/POST/api/uploads) | Upload a picture | Personal | App |\n| [<code>POST <span>/api</span><span>/drafts</span><span>/{id}</span><span>/send</span></code>](#tag/sending/POST/api/drafts/{id}/send) | Send a draft | Personal | App |\n| [<code>DELETE <span>/api</span><span>/drafts</span><span>/{id}</span><span>/send</span></code>](#tag/sending/DELETE/api/drafts/{id}/send) | Cancel a send | Personal | App |\n| [<code>GET <span>/api</span><span>/events</span></code>](#tag/live/GET/api/events) | Stream changes | Personal | App |\n| [<code>POST <span>/api</span><span>/conversations</span><span>/{id}</span><span>/presence</span></code>](#tag/live/POST/api/conversations/{id}/presence) | Set presence | Personal | App |\n| [<code>POST <span>/api</span><span>/devices</span></code>](#tag/devices/POST/api/devices) | Register a device | Personal | App |\n| [<code>DELETE <span>/api</span><span>/devices</span><span>/{id}</span></code>](#tag/devices/DELETE/api/devices/{id}) | Remove a device | Personal | App |\n| [<code>GET <span>/api</span><span>/labels</span></code>](#tag/labels/GET/api/labels) | List labels | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/labels</span></code>](#tag/labels/POST/api/labels) | Create a label | Team, personal (team settings) | Integration |\n| [<code>PATCH <span>/api</span><span>/labels</span><span>/{slug}</span></code>](#tag/labels/PATCH/api/labels/{slug}) | Update a label | Team, personal (team settings) | Integration |\n| [<code>DELETE <span>/api</span><span>/labels</span><span>/{slug}</span></code>](#tag/labels/DELETE/api/labels/{slug}) | Delete a label | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/members</span></code>](#tag/team/GET/api/members) | List members | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/mailboxes</span></code>](#tag/team/GET/api/mailboxes) | List mailboxes | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/mailboxes</span><span>/{id}</span><span>/members</span></code>](#tag/team/GET/api/mailboxes/{id}/members) | List a mailbox's members | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/saved_replies</span></code>](#tag/saved-replies/GET/api/saved_replies) | List saved replies | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/contacts</span></code>](#tag/contacts/GET/api/contacts) | Find a contact, or list contacts by a field | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/contacts</span><span>/suggest</span></code>](#tag/contacts/GET/api/contacts/suggest) | Suggest addresses for a recipient field | Personal | App |\n| [<code>POST <span>/api</span><span>/contacts</span></code>](#tag/contacts/POST/api/contacts) | Create or update a contact | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/contact_fields</span></code>](#tag/contact-fields/GET/api/contact_fields) | List contact fields | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/contact_fields</span></code>](#tag/contact-fields/POST/api/contact_fields) | Create a contact field | Team, personal (team settings) | Integration |\n| [<code>PATCH <span>/api</span><span>/contact_fields</span><span>/{key}</span></code>](#tag/contact-fields/PATCH/api/contact_fields/{key}) | Update a contact field | Team, personal (team settings) | Integration |\n| [<code>DELETE <span>/api</span><span>/contact_fields</span><span>/{key}</span></code>](#tag/contact-fields/DELETE/api/contact_fields/{key}) | Delete a contact field | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/card_sources</span></code>](#tag/contact-cards/GET/api/card_sources) | List card sources | Team, personal (team settings) | Integration |\n| [<code>POST <span>/api</span><span>/card_sources</span></code>](#tag/contact-cards/POST/api/card_sources) | Add a card source | Team, personal (team settings) | Integration |\n| [<code>PATCH <span>/api</span><span>/card_sources</span><span>/{id}</span></code>](#tag/contact-cards/PATCH/api/card_sources/{id}) | Update a card source | Team, personal (team settings) | Integration |\n| [<code>DELETE <span>/api</span><span>/card_sources</span><span>/{id}</span></code>](#tag/contact-cards/DELETE/api/card_sources/{id}) | Remove a card source | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/conversations</span><span>/{id}</span><span>/cards</span></code>](#tag/contact-cards/GET/api/conversations/{id}/cards) | Get a conversation's contact cards | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/webhooks</span></code>](#tag/webhooks/GET/api/webhooks) | List webhooks | Team, personal (team settings) | Integration |\n| [<code>POST <span>/api</span><span>/webhooks</span></code>](#tag/webhooks/POST/api/webhooks) | Add a webhook | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/webhooks</span><span>/{id}</span></code>](#tag/webhooks/GET/api/webhooks/{id}) | Get a webhook | Team, personal (team settings) | Integration |\n| [<code>PATCH <span>/api</span><span>/webhooks</span><span>/{id}</span></code>](#tag/webhooks/PATCH/api/webhooks/{id}) | Update a webhook | Team, personal (team settings) | Integration |\n| [<code>DELETE <span>/api</span><span>/webhooks</span><span>/{id}</span></code>](#tag/webhooks/DELETE/api/webhooks/{id}) | Remove a webhook | Team, personal (team settings) | Integration |\n| [<code>POST <span>/api</span><span>/webhooks</span><span>/{id}</span><span>/secret</span></code>](#tag/webhooks/POST/api/webhooks/{id}/secret) | Rotate a webhook's secret | Team, personal (team settings) | Integration |\n| [<code>POST <span>/api</span><span>/webhooks</span><span>/{id}</span><span>/test</span></code>](#tag/webhooks/POST/api/webhooks/{id}/test) | Send a test | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/webhooks</span><span>/{id}</span><span>/deliveries</span></code>](#tag/webhooks/GET/api/webhooks/{id}/deliveries) | List a webhook's deliveries | Team, personal (team settings) | Integration |\n| [<code>POST <span>/api</span><span>/webhooks</span><span>/{id}</span><span>/deliveries</span><span>/{delivery}</span><span>/redeliver</span></code>](#tag/webhooks/POST/api/webhooks/{id}/deliveries/{delivery}/redeliver) | Send a delivery again | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/conversation_fields</span></code>](#tag/conversation-fields/GET/api/conversation_fields) | List conversation fields | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/conversation_fields</span></code>](#tag/conversation-fields/POST/api/conversation_fields) | Create a conversation field | Team, personal (team settings) | Integration |\n| [<code>PATCH <span>/api</span><span>/conversation_fields</span><span>/{key}</span></code>](#tag/conversation-fields/PATCH/api/conversation_fields/{key}) | Update a conversation field | Team, personal (team settings) | Integration |\n| [<code>DELETE <span>/api</span><span>/conversation_fields</span><span>/{key}</span></code>](#tag/conversation-fields/DELETE/api/conversation_fields/{key}) | Delete a conversation field | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/autoresponder</span></code>](#tag/autoresponder/GET/api/autoresponder) | Get the autoresponder | Team, personal | Integration |\n| [<code>PATCH <span>/api</span><span>/autoresponder</span></code>](#tag/autoresponder/PATCH/api/autoresponder) | Update the autoresponder | Team, personal (team settings) | Integration |\n| [<code>GET <span>/api</span><span>/changes</span></code>](#tag/changes/GET/api/changes) | List changes | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/statistics</span></code>](#tag/statistics/GET/api/statistics) | Get statistics | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/inbound</span></code>](#tag/inbound/GET/api/inbound) | Get inbound email setup | Team, personal | Integration |\n| [<code>POST <span>/api</span><span>/files</span></code>](#tag/files/POST/api/files) | Upload a file | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/attachments</span><span>/{id}</span></code>](#tag/files/GET/api/attachments/{id}) | Get a file link | Team, personal | Integration |\n| [<code>GET <span>/api</span><span>/attachments</span><span>/{id}</span><span>/download</span></code>](#tag/files/GET/api/attachments/{id}/download) | Download a file | Signed link |  |\n| [<code>GET <span>/api</span><span>/draft_attachments</span><span>/{id}</span><span>/download</span></code>](#tag/files/GET/api/draft_attachments/{id}/download) | Download a draft's file | Signed link |  |\n| [<code>GET <span>/api</span><span>/saved_reply_files</span><span>/{id}</span><span>/download</span></code>](#tag/files/GET/api/saved_reply_files/{id}/download) | Download a saved reply's file | Signed link |  |\n| [<code>GET <span>/api</span><span>/uploads</span><span>/{hash}</span><span>/download</span></code>](#tag/files/GET/api/uploads/{hash}/download) | Download an uploaded picture | Signed link |  |\n| [<code>GET <span>/api</span><span>/avatars</span><span>/{hash}</span><span>/download</span></code>](#tag/files/GET/api/avatars/{hash}/download) | Download an avatar | Signed link |  |\n| [<code>GET <span>/api</span><span>/team</span><span>/logo</span><span>/{hash}</span><span>/download</span></code>](#tag/files/GET/api/team/logo/{hash}/download) | Download the team's logo | Signed link |  |\n| [<code>GET <span>/api</span><span>/remote</span></code>](#tag/files/GET/api/remote) | Fetch a remote picture | Signed link |  |\n\n## Changelog\n\nEvery change to the API, newest first. A breaking change applies only\nfrom its version on: see [Versions](#description/versions).\n\n### 2026-10-22\n\nThis is version `2026-10-22`. Version `2026-10-09` is supported until\n22 October 2027.\n\n- **Breaking.** In a `422`, each item in `errors` is an object with\n  `field`, `code` and `message`, not a string. This affects every\n  endpoint that can answer `422`.\n- **Breaking.** `POST /api/webhooks` and `PATCH /api/webhooks/{id}`\n  refuse the five event names from before `noun.verb` names:\n  `new_message`, `assigned`, `note_added`, `conversation_archived` and\n  `conversation_unarchived`. A webhook already subscribed to one is\n  still sent it until 22 October 2027.\n- The `Jelly-Version` header. Answers on version `2026-10-09` have\n  `Deprecation` and `Sunset` headers.\n- Webhooks can be managed through the API: `GET /api/webhooks`,\n  `POST /api/webhooks`, `GET`, `PATCH` and `DELETE /api/webhooks/{id}`,\n  `POST /api/webhooks/{id}/secret`, `POST /api/webhooks/{id}/test`,\n  `GET /api/webhooks/{id}/deliveries` and\n  `POST /api/webhooks/{id}/deliveries/{delivery}/redeliver`. Webhook\n  events are named like the change feed's kinds, such as\n  `message.received`.\n- Address suggestions for a recipient field:\n  `GET /api/contacts/suggest`.\n- A person's preferences: `preferences` in `GET /api/me`, and\n  `PATCH /api/me/preferences`.\n- The team's logo: `team.logo` in `GET /api/me`, and\n  `GET /api/team/logo/{hash}/download`.\n- The change feed: `GET /api/changes`.\n- File uploads: `POST /api/files`, for attachments on drafts and\n  inserted messages.\n- `GET /api/app`, for an app the team installed.\n- The `Idempotency-Key` header on every `POST`, `PATCH` and `DELETE`.\n\n### 2026-10-09\n\nThis is version `2026-10-09`: the API as this reference first published\nit.\n"
  },
  "servers": [
    {
      "url": "/",
      "description": "This Jelly"
    }
  ],
  "tags": [
    {
      "name": "Me",
      "description": "The person a personal token belongs to, and what this server supports."
    },
    {
      "name": "Apps",
      "description": "An app installed from a manifest reads its own installation here. See\n[Apps](/manual/advanced/apps) in the manual for the manifest and the\ninstall."
    },
    {
      "name": "Inbox and lists",
      "description": "One person's view of the mail: their inbox, their lists and the counts\nbeside them. These match the web app exactly. A team token gets `403\nneeds_person`.\n\n### What a row contains\n\nEvery list returns conversations as rows. A row includes:\n\n```json\n\"snippet\": \"Thanks for getting back to us so quickly…\",\n\"latest_note\": { \"author\": { \"type\": \"member\", \"id\": 3, \"name\": \"Mike\" },\n  \"text\": \"Rang her, the parcel is at the depot\", \"created_at\": \"2026-03-04T09:20:00Z\" },\n\"last_from\": { \"name\": \"Jane Customer\", \"email\": \"jane@customer.test\", \"avatar\": { \"…\": \"…\" } },\n\"last_from_me\": false,\n\"has_attachments\": true,\n\"stamp\": \"lz3k9f:0:-\"\n```\n\n- `latest_note` is the newest note when nothing newer has happened in\n  the conversation. Otherwise it is `null`.\n- `last_from_me` is `true` when someone on the team sent the last\n  message.\n- `has_attachments` counts files on messages, not on notes or unsent\n  drafts.\n- `stamp` changes whenever the conversation changes. Compare it with a\n  cached copy to know whether to fetch the thread again. Treat it as\n  opaque. Reactions, renamed labels and changed names do not change it.\n\nA personal token also gets the reader's own marks:\n\n```json\n\"unread\": true, \"read_at\": \"2026-03-04T09:00:00Z\", \"bookmarked\": false,\n\"following\": true, \"ignored\": false, \"mine\": true, \"claimable\": false\n```\n\n`ignored` means the reader set the conversation aside."
    },
    {
      "name": "Conversations",
      "description": "Listing, searching and reading conversations. A token only sees\nconversations in its own mailboxes. Any other conversation returns\n`404`.\n\n`status` is one of `open`, `archived`, `snoozed`, `spam` or `trash`.\nJelly shows open conversations in the inbox, so `inbox` also works as a\nfilter for `open`.\n\nA conversation that has been `spam` or `trash` for 30 days is deleted,\nwith everything in it. After that it returns `404`, and no change says\nso. The 30 days start when it moved there: its last\n`conversation.spammed` or `conversation.trashed` change, or its arrival\nfor a conversation that arrived as spam. Delete your copy then too.\n\n`contact` is the person the conversation is with: the contact on its\nfirst message.\n\n`external_id` is your own id for the conversation, such as an order or\na ticket in your system. Only one conversation can have a given\n`external_id`. A single conversation, and a row with `include=fields`,\nalways has the key, `null` until you set it. Other rows have it only\nwhen it is set.\n\n`fields` holds the conversation's values for the team's conversation\nfields (see Conversation fields), by key. A field with no value is not\nin `fields`. A single conversation always has `fields`. List and search\nrows have them only with `include=fields`."
    },
    {
      "name": "Conversation actions",
      "description": "Changing a conversation. Each action is recorded on the conversation's\ntimeline and triggers webhooks, as the same action in the web app does.\nEach returns the conversation after the change.\n\nAn action by a team token is recorded under the token's name."
    },
    {
      "name": "Your own marks",
      "description": "Marks that belong to one person: read, bookmarked, followed and set\naside. Each returns the conversation after the change. A team token gets\n`403 needs_person`."
    },
    {
      "name": "Notes",
      "description": "Internal notes on a conversation, and reactions to them. A team token\ncan read and write notes. Only a personal token can delete notes and\nreact to them.\n\n### Mentions\n\nMention a member with `@` and their handle. The handle is the part of\ntheir email address before the `@`, such as `@sarah`. If two active\nmembers share that part, use the whole address, such as\n`@sam@acme.test`. The whole address always works.\n\nA mention notifies the member and makes them follow the conversation. A\nmember who cannot see the conversation is not mentioned. In the stored\nnote, a mention becomes `<span class=\"jam-mention\">@Sarah Chen</span>`.\n\nHow the server finds mentions in a note's HTML:\n\n1. Only text is read. Tags, attributes and link text are ignored.\n2. A mention is `@` followed by letters, digits or `. _ % + -`,\n   optionally followed by `@` and a domain.\n3. The `@` must start a word: it is at the start of the text, or after\n   a character that is not a letter, digit or one of `. _ % + - / @ =`.\n4. The handle matches an active member's address, ignoring case. A\n   short handle shared by two members matches nobody.\n5. If the handle matches nobody and ends in `. _ % + -`, that last\n   character is dropped and the match is tried again. So\n   \"Thanks @sam.\" mentions sam."
    },
    {
      "name": "Messages",
      "description": "A single message."
    },
    {
      "name": "Drafts",
      "description": "Writing replies, new messages and forwards, with their files and\npictures. Everyone who can see a draft's mailbox can see the draft, but\nonly one person can edit it at a time.\n\nA team token can create, read and save drafts, but not send them. A\ndraft a team token creates is not held by anyone, so the first person\nto open it can finish it. Discarding a draft, its files, the heartbeat,\nreleasing the lock and sending all need a personal token.\n\n### Holding a draft\n\nOnly the person who holds a draft can change its files or send it. When\na personal token creates or saves a draft, it holds the draft and gets a\n`lock_token`. Send the `lock_token` with the requests that need it.\n\nThe hold lasts two minutes after the last save or heartbeat. `423\nlocked` means someone else holds the draft; the answer says who.\n\n### Recipients\n\n`to`, `cc` and `bcc` are arrays of addresses, such as\n`Jane Customer <jane@customer.test>`. You can also send one\ncomma-separated string. An address that cannot be read returns `422`\nand nothing is saved. Send `[]` to clear a field.\n\n### From address\n\n`from` is the address the draft will be sent from, in the same form as\n`GET /api/me` lists addresses. It is `null` if the team can no longer\nsend from that address. If the draft has no address chosen, `from` is\nthe default, with `\"default\": true`. Change it with `from_address_id`.\n\nAn address can be limited to some of the team. A personal token can\nonly choose an address its person may send from; any other returns\n`422`. A team token can choose any address. A draft from a limited\naddress shows that address to everyone, and only the people allowed can\nsend it. Limiting an address is part of the Royal Jelly plan; on Jelly,\nevery address is open to everyone.\n\n### Mailbox\n\n`mailbox_id` is the mailbox a new message's conversation starts in\nwhen it is sent. Without one, it is the default mailbox. Only people\nand tokens that can see that mailbox can see the draft, plus whoever\nstarted or last edited it. A reply's draft has `mailbox_id: null`: its\nconversation is already in its mailboxes.\n\n### The quoted message\n\n`quoted` is the message a reply answers, as a quote. It is only filled\nin until the draft is first saved. After that, the draft's body is the\nwhole reply and `quoted` is `\"\"`.\n\n### Pictures in the text\n\nTo put a picture in the text, upload it with `POST /api/uploads`, then\nadd it to the body using the `url` it returns:\n\n```html\n<p><img src=\"/uploads/9f86d081884c7d65…\" alt=\"shot.png\" class=\"jam-inline-image\"></p>\n```\n\n`inline_images` maps each `/uploads/…` address in the body to a signed\nlink you can use to display it. Keep `/uploads/…` in the body when you\nsave; the signed links expire.\n\n### Waiting to be sent\n\nWhen a draft is waiting to be sent, `scheduled_for` is when it will go\nand `scheduled_by` is who sent it. While it waits, it cannot be changed:\n`PATCH`, file changes, `send` and `DELETE` all return `423`. Cancel the\nsend with `DELETE /api/drafts/{id}/send` to edit it again."
    },
    {
      "name": "Sending",
      "description": "Sending a draft, and cancelling a send. Only a personal token can send.\nA team token gets `403 needs_person`."
    },
    {
      "name": "Live",
      "description": "Live updates, and who is looking at a conversation. Both need a\npersonal token."
    },
    {
      "name": "Devices",
      "description": "Phones and Macs registered for notifications. These need a personal token."
    },
    {
      "name": "Labels",
      "description": "The team's labels. Anyone can read them. Creating, changing and deleting\nlabels needs **change team settings**. To put a label on a conversation,\nuse `POST /api/conversations/{id}/labels`.\n\n`colour` is a colour from Jelly's palette."
    },
    {
      "name": "Team",
      "description": "The team's members and mailboxes."
    },
    {
      "name": "Saved replies",
      "description": "The team's saved replies. They are written in the web app; the API can\nread them and copy their files to a draft."
    },
    {
      "name": "Contacts",
      "description": "The people the team writes to.\n\nA contact's `fields` holds its values for the team's contact fields\n(see Contact fields), by key. A field with no value is not in `fields`.\n\n`external_id` is your own id for the contact, such as the id of the\ncustomer in your system. Only one contact can have a given\n`external_id`. A contact from these routes always has the key, `null`\nuntil you set it. A conversation's contact has it only when it is set."
    },
    {
      "name": "Contact fields",
      "description": "The facts the team keeps about each contact, such as a plan, an account\nid or a renewal date. A field has a `key`, a `label` and a `type`.\nAnyone can read the fields. Creating, changing and deleting them needs\n**change team settings**. To set a contact's value, send `fields` to\n`POST /api/contacts`.\n\nThe `key` is the field's id. It is how `fields` names the field on a\ncontact, and how search names it (`field:plan=pro`). It cannot change.\nThe `label` is what people read, and can change.\n\nThe types, and the values they hold:\n\n| Type | Value |\n|---|---|\n| `text` | A string. |\n| `number` | A number. Store money as a whole number of cents. |\n| `date` | A string, `YYYY-MM-DD`. |\n| `boolean` | `true` or `false`. |\n| `url` | An `http` or `https` URL. |\n| `choice` | One of the field's `options`. |"
    },
    {
      "name": "Contact cards",
      "description": "A contact card is what one of your own systems knows about a\nconversation's contact, drawn beside the conversation in Jelly. Jelly\nasks each card source with a signed `POST` and draws the JSON it\nanswers. What a source is sent and must answer is in\n[Contact cards](/manual/advanced/contact-cards#the-request) in the\nmanual.\n\nContact cards are part of the Royal Jelly plan. On the Jelly plan, a\nteam can list, turn off and remove its sources. Anything else here\nanswers `403` with the error `plan`."
    },
    {
      "name": "Conversation fields",
      "description": "The facts the team keeps about each conversation, such as the order\nit is about, the customer's plan or the app version they wrote from. A\nfield has a `key`, a `label` and a `type`, the same types as contact\nfields (see Contact fields). Anyone can read the fields. Creating,\nchanging and deleting them needs **change team settings**.\n\nTo set a conversation's values, send `fields` to\n`PATCH /api/conversations/{id}`, or with the message to\n`POST /api/messages`. Rules can match on them and set them.\n\nThe `key` is the field's id. It is how `fields` names the field, how a\nlist filters by it (`field.order=A-1234`) and how search finds it\n(`field:order=A-1234`). It cannot change. A conversation field and a\ncontact field can have the same key; search then matches either."
    },
    {
      "name": "Autoresponder",
      "description": "The team's default [autoresponder](/manual/advanced/autoresponder). It\nanswers every message that no rule sends a different autoresponse to.\nAutoresponders sent by rules are not in the API."
    },
    {
      "name": "Changes",
      "description": "Everything that happens to the team's conversations, messages, notes\nand contacts, in order. Use it to keep a copy of the team's mail up to\ndate, or to react to changes, without polling every list."
    },
    {
      "name": "Statistics",
      "description": "The team's figures, as shown on the Statistics page."
    },
    {
      "name": "Inbound",
      "description": "How this Jelly receives email, for setting up mail delivery."
    },
    {
      "name": "Files",
      "description": "Files and pictures.\n\nTo attach a file to an inserted message, a note or a draft, upload it\nfirst with `POST /files`, then give its `id` to the route that attaches\nit.\n\nMost of these are signed links. A signed link needs no token: its\n`expires` and `signature` are the credential. Threads, drafts and saved\nreplies give you signed links already. A changed link returns `404`, and\nan expired one returns `410`."
    },
    {
      "name": "Webhooks",
      "description": "A webhook sends a JSON request to your URL when something happens in\nJelly. Set webhooks up on **Settings → Webhooks**, or with the endpoints\nbelow. See [Webhooks](/manual/advanced/webhooks) in the manual.\n\n### Events\n\nEvery kind in the [change feed](#tag/changes) is an event, with the\nsame name: `conversation.archived`, `message.received`, `note.added`,\n`message.delivery_updated`, `contact.updated` and the rest. What each\none means is listed under **Kinds** there. A webhook gets only the\nevents it subscribes to.\n\nThe five event names from before the change feed still work:\n`new_message` (`message.received` and `message.sent`, not counting the\nautoresponder's replies), `assigned` (`conversation.assigned` and\n`conversation.claimed`), `note_added`, `conversation_archived` and\n`conversation_unarchived`. Each keeps its old `data`. Only version\n`2026-10-09` can subscribe a webhook to them; from `2026-10-22` they\nare refused with `422`. A webhook already subscribed to one is sent it\nuntil 22 October 2027, when they are removed. Use the new names.\n\n`ping` is sent only by [Send a test](#tag/webhooks/POST/api/webhooks/{id}/test).\n\n### Mailboxes\n\nA webhook hears every mailbox (`all_mailboxes: true`), or only the\nmailboxes in `mailbox_ids`. A webhook with `mailbox_ids` gets an event\nabout a conversation only when the conversation is in one of those\nmailboxes. When a conversation moves out of them, the webhook gets\n`conversation.mailboxes_changed`, and `data.conversation` has only its\n`id`. Contact events go to every webhook that subscribes to them,\nbecause contacts belong to the whole team.\n\nA team token that can see only some mailboxes can only make webhooks\nfor those mailboxes. It cannot see or change a webhook that hears any\nother mailbox.\n\nNothing is sent about a conversation in spam, except\n`conversation.spammed` itself. Nothing is sent for mail brought in by an\nimport.\n\n### The request\n\n```json\n{ \"event\": \"conversation.archived\", \"seq\": 4182, \"created_at\": \"2026-03-01T10:30:00Z\", \"data\": { … } }\n```\n\n`seq` is the change's place in the [change feed](#tag/changes).\n`created_at` is when the change happened. `data` uses the same objects\nas the REST API, without `avatar` or `stamp`, as they are when the\nrequest is made. `data.actor` is who did it, and `data.summary` is the\nchange's summary from the feed.\n\nA request from a [sandbox team](/manual/advanced/api#sandbox) also has\n`\"sandbox\": true`. Other teams' requests do not have the field.\n\n### Headers\n\n| Header | |\n| --- | --- |\n| `X-Jelly-Event` | The event |\n| `X-Jelly-Timestamp` | Unix seconds, when this try was sent |\n| `X-Jelly-Signature` | `sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret |\n| `X-Jelly-Delivery` | The delivery's id, the same on every try of it |\n| `Authorization` | Basic auth, if the webhook has a username or password |\n\nCheck the signature against the raw request body, before parsing it:\n\n```ruby\n\"sha256=\" + OpenSSL::HMAC.hexdigest(\"SHA256\", signing_secret, raw_body)\n```\n\n### Responding\n\nRespond with a `2xx` status within ten seconds. Any other answer, or\nnone, is a failure, and the delivery is tried again: ten minutes after\nthe first try, then at 30 minutes, 1, 2, 4, 8 and 16 hours, and at 24,\n36 and 48 hours, after which Jelly gives up and emails the team's\nAdmins. A retry carries the same body, byte for byte, re-signed, with\nthe same `X-Jelly-Delivery`. Ten deliveries given up on inside a day\nswitch the webhook off; see\n[Delivery](/manual/advanced/webhooks#delivery) in the manual.\n\nJelly sends only to addresses on the public internet. It checks every\naddress the URL's host resolves to, as it connects. If any of them is\nprivate, loopback, link-local or otherwise internal, nothing is sent,\nand the try is a failure with the error `the address is private`. A\nself-hosted instance can allow named networks; see [Receivers on your\nown network](/manual/advanced/webhooks#receivers-on-your-own-network).\nJelly does not follow redirects: a `3xx` answer is a failure. Jelly\nreads at most 64 KB of your answer.\n\n### What is guaranteed\n\n- A delivery is sent at least once. The same delivery can arrive more\n  than once. Keep the `X-Jelly-Delivery` ids you have seen and ignore a\n  repeat.\n- Order is not guaranteed. Two changes made close together can arrive\n  in either order, and a retry arrives after everything sent since. Use\n  `seq` to put events in order: a larger `seq` happened later.\n- A change can be missed: while the webhook is off, while the team's\n  plan has no webhooks, after Jelly gives up on a delivery, or when\n  Jelly restarts in the moment after a change. To catch up, keep the\n  largest `seq` you have handled and call\n  [GET /changes](#tag/changes/GET/api/changes) with `since` set to it. The\n  feed keeps 30 days.\n- Deliveries are kept for 30 days. A delivery can be sent again with\n  [redeliver](#tag/webhooks/POST/api/webhooks/{id}/deliveries/{delivery}/redeliver)\n  during that time."
    }
  ],
  "paths": {
    "/api/me": {
      "get": {
        "tags": [
          "Me"
        ],
        "summary": "Get the signed-in person and server capabilities",
        "description": "Returns the member, the team, the addresses they can send from, their\nsignature, the undo-send window and the server's capabilities.\n\n`member.name` is the name the person set, and may be empty.\n\n`addresses` is the set of addresses this person can send from. It is the\nsame set offered on the web and in the apps. An address that only some\nof the team may send from is listed only for them.\n\nEach address's `from_name` is the name a message from this person shows\nin its From line. It follows the team's **From name on outgoing mail**\nsetting, reported in `team.from_name_format`:\n\n| `from_name_format` | Example `from_name` |\n| --- | --- |\n| `person_address` | Sarah Chen (Support) |\n| `person` | Sarah Chen |\n| `address` | Support |\n\nAn empty `from_name` means the From line shows only the address. Use\n`from_name` as given rather than building it yourself.\n\n`team.logo` is the logo the team set on the web, or `null` when it has\nnone. `url` is a signed link that needs no token and expires after at\nleast a day; it changes every hour, so do not cache by it. `version`\nchanges only when the logo does. Keep the picture by `version`, and\ndownload it again when `version` changes.\n\n`send_delay_seconds` is how long a send waits before it goes, so it can\nbe undone.\n\n`capabilities` holds what can differ from one server to another. Every\nother feature described here is on every server, so a client can use it\nwithout checking.\n\n| Name | Meaning |\n| --- | --- |\n| `push` | `true` only where there is a relay to send a device's notifications through |\n\n`sandbox` is `true` on a sandbox team: its customers are pretend, and\nnothing it sends leaves. See\n[A sandbox to build against](/manual/advanced/api#sandbox).\n\n`preferences` holds the person's own choices from **Preferences** on\nthe web, the ones an app needs to draw its screens the same way.\n`send_archive_first` says which send button leads under a reply:\n**Send & archive** when `true`, **Send** when `false`. The leading\nbutton is the one ⌘Enter presses. `forward_go_back_first` is the same\nchoice under a forward, between **Forward & go back** and **Forward**.\nBoth are `false` for a person who has never changed them. More may be\nadded.\n\n`grant` is the sign-in the token belongs to: the app's `client_id`, the\nname it is listed under in **Where you're signed in**, and when it\nbegan. For an app the team registered, `grant.scopes` lists what the\nperson allowed it to do, so the app can tell before it asks. Any token\nmay call this endpoint, whatever its scopes.",
        "operationId": "get_me",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "member": {
                      "$ref": "#/components/schemas/Member",
                      "description": "The person the token belongs to."
                    },
                    "team": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "from_name_format": {
                          "type": "string",
                          "enum": [
                            "person_address",
                            "person",
                            "address"
                          ],
                          "description": "How the name on sent mail is made. More values may be added."
                        },
                        "logo": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "properties": {
                            "url": {
                              "type": "string",
                              "description": "A signed link to the logo, a square PNG. It works without a token, for at least a day."
                            },
                            "version": {
                              "type": "string",
                              "description": "Changes when the logo does."
                            }
                          },
                          "required": [
                            "url",
                            "version"
                          ],
                          "description": "The team's logo, or `null` when it has none."
                        }
                      },
                      "required": [
                        "name",
                        "from_name_format",
                        "logo"
                      ]
                    },
                    "addresses": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SendingAddress"
                      },
                      "description": "The addresses this person can send from."
                    },
                    "signature": {
                      "type": "string",
                      "description": "This person's signature, as HTML."
                    },
                    "send_delay_seconds": {
                      "type": "integer",
                      "description": "The undo window: how long a sent draft waits before it goes."
                    },
                    "sandbox": {
                      "type": "boolean",
                      "description": "`true` on a sandbox team: nothing in it is real and no email leaves. Left out otherwise."
                    },
                    "capabilities": {
                      "type": "object",
                      "properties": {
                        "push": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "push"
                      ],
                      "description": "What this server can do that another may not."
                    },
                    "preferences": {
                      "type": "object",
                      "properties": {
                        "send_archive_first": {
                          "type": "boolean",
                          "description": "`true` when **Send & archive** leads under a reply, and is the one ⌘Enter presses; `false` when **Send** does."
                        },
                        "forward_go_back_first": {
                          "type": "boolean",
                          "description": "`true` when **Forward & go back** leads under a forward; `false` when **Forward** does."
                        }
                      },
                      "required": [
                        "send_archive_first",
                        "forward_go_back_first"
                      ],
                      "description": "This person's own preferences. `PATCH /me/preferences` changes them."
                    },
                    "grant": {
                      "type": "object",
                      "properties": {
                        "client_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "What a registered app's grant may do. Not for Jelly's own apps."
                        }
                      },
                      "required": [
                        "client_id",
                        "name",
                        "created_at"
                      ],
                      "description": "The sign-in this token came from."
                    }
                  },
                  "required": [
                    "member",
                    "team",
                    "addresses",
                    "signature",
                    "send_delay_seconds",
                    "capabilities",
                    "preferences"
                  ]
                },
                "example": {
                  "member": {
                    "id": 3,
                    "name": "Sarah Chen",
                    "email": "sarah@acme.test",
                    "role": "admin",
                    "avatar": {
                      "url": null,
                      "initials": "SC",
                      "tint": {
                        "light": {
                          "background": "#5e35b1",
                          "text": "#ffffff"
                        },
                        "dark": {
                          "background": "#5e35b1",
                          "text": "#ffffff"
                        }
                      }
                    }
                  },
                  "team": {
                    "name": "Acme",
                    "from_name_format": "person_address",
                    "logo": {
                      "url": "https://jelly.acme.test/api/team/logo/5f2c9a…/download?expires=1790089200&signature=f344…",
                      "version": "5f2c9a0d81e7"
                    }
                  },
                  "addresses": [
                    {
                      "id": 1,
                      "address": "support@acme.test",
                      "name": "Support",
                      "from_name": "Sarah Chen (Support)"
                    }
                  ],
                  "signature": "<p>Sarah</p>",
                  "send_delay_seconds": 10,
                  "capabilities": {
                    "push": true
                  },
                  "sandbox": false,
                  "preferences": {
                    "send_archive_first": false,
                    "forward_go_back_first": false
                  },
                  "grant": {
                    "client_id": "jelly-ios",
                    "name": "Jelly for iPhone",
                    "created_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/me/preferences": {
      "patch": {
        "tags": [
          "Me"
        ],
        "summary": "Update the signed-in person's preferences",
        "description": "Changes the person's own preferences, the same ones **Preferences**\nchanges on the web. A change made here shows on the web, and a change\nmade there shows in `GET /me`.\n\nSend only the preferences to change: one left out is left as it was.\nThe response is every preference after the change.\n\nA body that names no preference, names something that is not one, or\ngives one a value other than `true` or `false` is refused with a `422`,\nand nothing is changed. Only a person's own token may call this, since\na team token has no person to have preferences.",
        "operationId": "patch_me_preferences",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "send_archive_first": {
                    "type": "boolean"
                  },
                  "forward_go_back_first": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "send_archive_first": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Every preference, after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "send_archive_first": {
                      "type": "boolean",
                      "description": "`true` when **Send & archive** leads under a reply, and is the one ⌘Enter presses; `false` when **Send** does."
                    },
                    "forward_go_back_first": {
                      "type": "boolean",
                      "description": "`true` when **Forward & go back** leads under a forward; `false` when **Forward** does."
                    }
                  },
                  "required": [
                    "send_archive_first",
                    "forward_go_back_first"
                  ]
                },
                "example": {
                  "send_archive_first": true,
                  "forward_go_back_first": false
                }
              }
            }
          },
          "422": {
            "description": "The body names no preference, names something that is not one, or gives one a value that is not `true` or `false`. Nothing is changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/app": {
      "get": {
        "tags": [
          "Apps"
        ],
        "summary": "Get the installation of the app this token belongs to",
        "description": "Returns the installation of the app the token belongs to: its client,\nits webhook, its card source and the mailboxes the token can see.\n\nCall it with any personal token the app holds. A token of an app that\nwas registered by hand, or of one of Jelly's own apps, gets a 404.\n\n`scopes` is what the app may ask a person for. `granted_scopes` is what\nthis token's person allowed. A scope the app gained in an update is in\n`scopes` and not in `granted_scopes` until the person allows the app\nagain. Any token can call this route, whatever its scopes.\n\n`webhook` and `card_source` are `null` when the manifest asked for none,\nor when an Admin has since deleted them. Neither carries its secret.\nThe secrets are sent to the app's `install_url` once, at install.\n\n`mailboxes` is what this token can see, which is what its person can\nsee. The webhook hears the mailboxes in `webhook.mailbox_ids`, or every\nmailbox when `webhook.all_mailboxes` is `true`. The Admin chose them at\ninstall.",
        "operationId": "get_app",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "installation": {
                      "$ref": "#/components/schemas/Installation"
                    }
                  },
                  "required": [
                    "installation"
                  ]
                },
                "example": {
                  "installation": {
                    "id": 4,
                    "name": "Acme Console",
                    "client_id": "app_k3f9zq2m7p4x8r1t6w5y",
                    "scopes": [
                      "read",
                      "write"
                    ],
                    "granted_scopes": [
                      "read"
                    ],
                    "manifest_url": "https://console.acmetools.test/jelly/manifest.json",
                    "manifest_hash": "9f2c…",
                    "webhook": {
                      "id": 2,
                      "url": "https://console.acmetools.test/jelly/events",
                      "events": [
                        "conversation.archived",
                        "note.added"
                      ],
                      "all_mailboxes": false,
                      "mailbox_ids": [
                        4
                      ],
                      "active": true,
                      "deactivated_at": null,
                      "basic_auth_username": "",
                      "has_basic_auth_password": false,
                      "consecutive_failures": 0,
                      "last_success_at": "2026-03-01T10:31:00Z",
                      "last_error": null,
                      "last_error_at": null,
                      "created_at": "2026-03-01T09:00:00Z",
                      "updated_at": "2026-03-01T09:00:00Z"
                    },
                    "card_source": {
                      "id": 3,
                      "url": "https://console.acmetools.test/jelly/card",
                      "active": true
                    },
                    "mailboxes": [
                      {
                        "id": 1,
                        "name": "Support",
                        "default": true
                      }
                    ],
                    "installed_at": "2026-03-01T09:00:00Z",
                    "updated_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The token's app was not installed from a manifest.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found",
                  "message": "This token's app was not installed from a manifest."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/inbox": {
      "get": {
        "tags": [
          "Inbox and lists"
        ],
        "summary": "Get the inbox",
        "description": "Returns the inbox in four sections: Brand new, Mine, Unassigned, and\nAssigned to someone else. Each section has up to 25 conversations.\n`next` is a link to the full section as a paginated list.\n\nConversations you set aside appear in your Unassigned section.\n\n`colour` is the section heading's colour, for light and dark themes.",
        "operationId": "get_inbox",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "mailbox",
            "in": "query",
            "description": "Only conversations in this mailbox.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sections": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InboxSection"
                      }
                    }
                  },
                  "required": [
                    "sections"
                  ]
                },
                "example": {
                  "sections": [
                    {
                      "key": "new",
                      "title": "Brand new",
                      "total": 3,
                      "unread": 1,
                      "colour": {
                        "wash": {
                          "light": "#ddf0fd",
                          "dark": "#052c48"
                        },
                        "ink": {
                          "light": "#074069",
                          "dark": "#c6e5fa"
                        }
                      },
                      "conversations": [],
                      "next": "https://jelly.acme.test/api/lists/all?assigned=nobody"
                    },
                    {
                      "key": "mine",
                      "title": "Mine",
                      "total": 0,
                      "unread": 0,
                      "conversations": [],
                      "next": null
                    },
                    {
                      "key": "unassigned",
                      "title": "Unassigned",
                      "total": 0,
                      "unread": 0,
                      "conversations": [],
                      "next": null
                    },
                    {
                      "key": "others",
                      "title": "Assigned to someone else",
                      "total": 0,
                      "unread": 0,
                      "conversations": [],
                      "next": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/lists/{list}": {
      "get": {
        "tags": [
          "Inbox and lists"
        ],
        "summary": "Get a list",
        "description": "Returns one list, 25 conversations per page.\n\nIn `drafts`, a draft for a new message has no conversation yet: its row\nhas `id: null` and a `draft` object. Open it with\n`GET /api/drafts/{id}`. In `drafts` and `scheduled`, a reply's row also\nhas a `draft` object.\n\n`scheduled` lists messages waiting to be sent, soonest first.",
        "operationId": "get_lists_list",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "list",
            "in": "path",
            "description": "`all`, `bookmarks`, `archived`, `snoozed`, `spam`, `trash`, `sent`, `drafts`, `scheduled`, or `labels/{slug}`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "assigned",
            "in": "query",
            "description": "Only conversations assigned to you, to others, or to nobody.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "me",
                "others",
                "nobody"
              ]
            }
          },
          {
            "name": "aside",
            "in": "query",
            "description": "`1`: only conversations you set aside.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `next_cursor` from the previous page.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ListRow"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Send it back as `?cursor=` for the next page. `null` on the last."
                    }
                  },
                  "required": [
                    "conversations",
                    "next_cursor"
                  ]
                },
                "example": {
                  "conversations": [
                    {
                      "id": null,
                      "subject": "(No subject)",
                      "last_activity_at": "2026-03-04T09:12:00Z",
                      "has_draft": true,
                      "has_attachments": false,
                      "snippet": "Notes for the wholesale sheet…",
                      "draft": {
                        "id": 42,
                        "conversation_id": null,
                        "subject": "",
                        "to": [],
                        "cc": [],
                        "updated_at": "2026-03-04T09:12:00Z",
                        "locked": false,
                        "url": "https://jelly.acme.test/compose?draft=42",
                        "scheduled_for": null,
                        "scheduled_by": null
                      }
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "404": {
            "description": "No such list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/sidebar": {
      "get": {
        "tags": [
          "Inbox and lists"
        ],
        "summary": "Get counts, mailboxes and labels",
        "description": "Returns the counts, mailboxes and labels shown beside the lists.\n`drafts` and `scheduled` count the drafts the person can see, including\nother people's.",
        "operationId": "get_sidebar",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "counts": {
                      "type": "object",
                      "properties": {
                        "inbox": {
                          "type": "integer"
                        },
                        "archived": {
                          "type": "integer"
                        },
                        "drafts": {
                          "type": "integer"
                        },
                        "scheduled": {
                          "type": "integer"
                        },
                        "snoozed": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "inbox",
                        "archived",
                        "drafts",
                        "scheduled"
                      ],
                      "description": "How many conversations each list holds for this person."
                    },
                    "bookmarks": {
                      "type": "integer"
                    },
                    "mailboxes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Mailbox"
                      }
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Label"
                      }
                    }
                  },
                  "required": [
                    "counts",
                    "bookmarks",
                    "mailboxes",
                    "labels"
                  ]
                },
                "example": {
                  "counts": {
                    "inbox": 3,
                    "drafts": 2,
                    "scheduled": 1,
                    "snoozed": 0
                  },
                  "bookmarks": 2,
                  "mailboxes": [
                    {
                      "id": 1,
                      "name": "Support",
                      "default": true
                    }
                  ],
                  "labels": [
                    {
                      "id": "billing",
                      "name": "Billing",
                      "colour": "lemon",
                      "created_at": "2026-01-02T10:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List conversations",
        "description": "Lists conversations, newest activity first. Spam, trash and vaulted\nconversations are left out unless you ask for them with `status`.\n\n- `external_id` returns the one conversation with that external id.\n- `field.<key>=<value>` returns conversations whose field has that\n  value, such as `field.plan=enterprise`. Text is matched whatever its\n  capitals. An empty value matches any value. Several filters must all\n  match. A key that is not a conversation field is a `422`.\n- `include=fields` adds `fields` to each row.\n\nThe same filters work on `GET /conversations/search`.",
        "operationId": "get_conversations",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Only conversations with this status. `inbox` means `open`. Without it, spam, trash and the vault are left out.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "archived",
                "snoozed",
                "spam",
                "trash",
                "vaulted"
              ]
            }
          },
          {
            "name": "label_id",
            "in": "query",
            "description": "Only conversations with this label.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mailbox_id",
            "in": "query",
            "description": "Only conversations in this mailbox.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Only the conversation with this external id.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field.<key>",
            "in": "query",
            "description": "Only conversations whose field `<key>` has this value, such as `field.plan=enterprise`. Empty: any value. Give several to match them all.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "`fields`: each row includes `fields`. Without it, rows leave them out.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "fields"
              ]
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "description": "Only conversations changed at or after this time (RFC 3339), oldest change first. Spam and trash are included unless `status` says otherwise.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Results per page, 1 to 100. The default is 50.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `next_cursor` from the previous page.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Send it back as `?cursor=` for the next page. `null` on the last."
                    }
                  },
                  "required": [
                    "conversations",
                    "next_cursor"
                  ]
                },
                "example": {
                  "conversations": [
                    {
                      "id": "broken-widget-k3f9zq",
                      "external_id": "order-1234",
                      "subject": "Broken widget",
                      "status": "open",
                      "fields": {
                        "plan": "enterprise"
                      },
                      "…": "…"
                    }
                  ],
                  "next_cursor": "MTcwMDAwMDAwMA"
                }
              }
            }
          },
          "422": {
            "description": "A `status` it does not know, a `limit` out of range, a `field.<key>` that is not a conversation field, a value that field could not hold, or an `updated_since` that is not a time.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/search": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Search conversations",
        "description": "Searches conversations. `q` takes the same text and operators as the\nweb app's search box; see\n[Finding conversations](/manual/basics/finding#operators).\n\n- Spam, trash and vaulted conversations are left out unless `q` has\n  `in:spam`, `in:trash` or `in:vault`, or you set `status`.\n- `label_id` and `mailbox_id` narrow the search further.\n- `is:` operators (such as `is:unread`) need a personal token. A team\n  token gets `422`.\n- A `q` with only operators returns every match, newest first. A `q`\n  with words returns the best matches first, up to 200.\n\nResults are rows, as lists return them. With `include_matches=true`,\neach has a `match`: the matching text, with the matched words marked.",
        "operationId": "get_conversations_search",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "The search, with operators, such as `refund from:jane@customer.test` or `is:unread`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only conversations with this status. `inbox` means `open`. Without it, spam, trash and the vault are left out.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "archived",
                "snoozed",
                "spam",
                "trash",
                "vaulted"
              ]
            }
          },
          {
            "name": "label_id",
            "in": "query",
            "description": "Only conversations with this label.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mailbox_id",
            "in": "query",
            "description": "Only conversations in this mailbox.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Only the conversation with this external id.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field.<key>",
            "in": "query",
            "description": "Only conversations whose field `<key>` has this value, such as `field.plan=enterprise`. Empty: any value. Give several to match them all.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include",
            "in": "query",
            "description": "`fields`: each row includes `fields`. Without it, rows leave them out.",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "fields"
              ]
            }
          },
          {
            "name": "include_matches",
            "in": "query",
            "description": "`true`: each result includes `match`, the text that matched.",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Results per page, 1 to 100. The default is 50.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `next_cursor` from the previous page.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Send it back as `?cursor=` for the next page. `null` on the last."
                    }
                  },
                  "required": [
                    "conversations",
                    "next_cursor"
                  ]
                },
                "example": {
                  "conversations": [
                    {
                      "id": "broken-widget-k3f9zq",
                      "subject": "Broken widget",
                      "match": "the <mark>widget</mark> arrived broken",
                      "…": "…"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "422": {
            "description": "No `q`, or `is:` from a team token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get a conversation",
        "description": "Returns one conversation with up to 20 recent messages.\n\nIt also says how a reply would start:\n\n```json\n\"reply_from\": { \"id\": 2, \"address\": \"sales@acme.test\", \"name\": \"Sales\" },\n\"reply\": { \"message_id\": \"k3f9zq\", \"to\": [\"Sam <sam@example.test>\"], \"cc\": [] }\n```\n\n- `reply_from` is the address a reply is sent from, or `null` if the\n  team cannot send at all.\n- `reply` is who a reply goes to, and which message it answers. It is\n  empty when there is nothing to answer.\n\n`POST .../draft_reply` uses these when you give it no recipients.",
        "operationId": "get_conversations_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Conversations"
        ],
        "summary": "Update a conversation's fields and external id",
        "description": "Sets a conversation's `fields` and `external_id`. Nothing else can be\nchanged here.\n\n- `fields` sets values by key. Keys you leave out keep their values, and\n  `null` clears one. A key that is not a conversation field, or a value\n  of the wrong type, is a `422` that names the field, and nothing is\n  changed.\n- `external_id` sets the external id. `null` or `\"\"` clears it. If\n  another conversation has it, the answer is `409` and nothing is\n  changed.\n\nEach value that changes adds a line to the conversation's timeline,\nsuch as \"set Plan to “enterprise”\". It does not move the conversation\nup the inbox.",
        "operationId": "patch_conversations_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fields": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Field values, by the field's key. A key left out keeps its value. `null` clears one."
                  },
                  "external_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Your own id for this conversation, up to 255 characters. `null` or `\"\"` clears it."
                  }
                }
              },
              "example": {
                "external_id": "order-1234",
                "fields": {
                  "plan": "enterprise",
                  "app_version": null
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The conversation, with its `fields`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Another conversation has this `external_id`. Nothing is changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A key in `fields` that is not a conversation field, a value of the wrong type, or any other key in the body. Nothing is changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/thread": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get a conversation's thread",
        "description": "Returns the whole conversation as the web app shows it to this person:\nthe conversation, `seen_at` (the person's previous visit), `stamp`,\n`links_expire_at`, `presence`, and `items` in order.\n\nReading a thread does not mark it read. Use `POST .../read` for that.\n\n### Items\n\nEach item has a `kind`:\n\n- `message`: a message. `html` is cleaned and has quoted text folded\n  into a `<details class=\"jam-quote\">` element. `remote_images_blocked`\n  counts blocked pictures; add `?remote_images=1` to show them.\n- `note`: an internal note, with its `attachments`, `reactions` and\n  `deletable_until`.\n- `activity`: a line of the timeline, such as \"assigned this to Sarah\".\n  `actor` and `target` say who, so you can write your own sentence.\n- `scheduled`: a reply waiting to be sent. `scheduled_for` is when it\n  goes. `DELETE /api/drafts/{draft_id}/send` cancels it.\n- `draft`: a reply being written. `locked` says whether someone has it\n  open, and `editor` who. Use `updated_at` as `if_updated_at` when you\n  save it.\n\nA thread ends with at most one `scheduled` or `draft` item.\n\n### Links in a thread\n\nFiles, inline pictures and remote pictures in a thread use signed links\nthat need no token, so an image view can load them directly. Signed\nlinks expire. `links_expire_at` is the earliest expiry in the response.\nAfter that time, fetch the thread again without `If-None-Match`. A\n`410` from a link means the same thing.\n\n### Caching\n\nThe response has an `ETag`. Send it back as `If-None-Match`; if nothing\nhas changed, the answer is `304` with no body. Treat the `ETag` as\nopaque. It also changes for things that do not change `stamp`, such as\nreactions and drafts.\n\n### Presence\n\n`presence` lists the other people who have the conversation open now,\nand whether each is `viewing`, `replying` or `commenting` (writing a\nnote). It never includes you.",
        "operationId": "get_conversations_id_thread",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "remote_images",
            "in": "query",
            "description": "`1`: show pictures from other websites.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "description": "The `ETag` of your cached copy. If nothing changed, the answer is `304`.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/ThreadConversation"
                    },
                    "seen_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When this person last saw the conversation, before this visit."
                    },
                    "stamp": {
                      "type": "string",
                      "description": "Changes when the conversation changes."
                    },
                    "links_expire_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the signed links in this answer stop working. Fetch the thread again after it."
                    },
                    "presence": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "member": {
                            "$ref": "#/components/schemas/Actor"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "viewing",
                              "replying",
                              "commenting"
                            ]
                          }
                        },
                        "required": [
                          "member",
                          "status"
                        ]
                      },
                      "description": "Who else has the conversation open."
                    },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ThreadItem"
                      },
                      "description": "The thread, oldest first."
                    }
                  },
                  "required": [
                    "conversation",
                    "seen_at",
                    "stamp",
                    "links_expire_at",
                    "presence",
                    "items"
                  ]
                },
                "example": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget",
                    "…": "…",
                    "reply_from": {
                      "id": 1,
                      "address": "support@acme.test",
                      "name": "Support"
                    },
                    "reply": {
                      "message_id": "k3f9zq",
                      "to": [
                        "Jane Customer <jane@customer.test>"
                      ],
                      "cc": []
                    }
                  },
                  "seen_at": "2026-03-04T08:30:00Z",
                  "stamp": "lz3k9f:0:-",
                  "links_expire_at": "2026-03-04T09:05:00Z",
                  "presence": [
                    {
                      "member": {
                        "type": "member",
                        "id": 4,
                        "name": "Bob"
                      },
                      "status": "replying"
                    }
                  ],
                  "items": [
                    {
                      "kind": "message",
                      "id": "k3f9zq",
                      "direction": "inbound",
                      "from": {
                        "name": "Jane Customer",
                        "email": "jane@customer.test"
                      },
                      "to": [],
                      "cc": [],
                      "sent_at": "2026-03-04T08:00:00Z",
                      "html": "<p>My widget arrived broken.</p>",
                      "text": "My widget arrived broken.",
                      "remote_images_blocked": 0,
                      "attachments": [
                        {
                          "id": 9,
                          "filename": "photo.jpg",
                          "url": "https://jelly.acme.test/api/attachments/9/download?expires=1772615100&signature=…"
                        }
                      ],
                      "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq?focus=m-k3f9zq#m-k3f9zq"
                    },
                    {
                      "kind": "note",
                      "id": "n8x2",
                      "author": {
                        "type": "member",
                        "id": 3,
                        "name": "Sarah Chen"
                      },
                      "html": "<p>Sending a new one</p>",
                      "at": "2026-03-04T08:10:00Z",
                      "attachments": [],
                      "reactions": [],
                      "deletable_until": null,
                      "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq?focus=n-n8x2#n-n8x2"
                    },
                    {
                      "kind": "activity",
                      "verb": "assigned",
                      "line": "assigned this to <span data-member=\"3\" data-you=\"you\">Sarah Chen</span>",
                      "actor": {
                        "type": "member",
                        "id": 3,
                        "name": "Sarah Chen"
                      },
                      "target": {
                        "type": "member",
                        "id": 3,
                        "name": "Sarah Chen"
                      },
                      "at": "2026-03-04T08:11:00Z"
                    }
                  ]
                }
              }
            }
          },
          "304": {
            "description": "Not changed since your cached copy."
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/markdown": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get a conversation as markdown",
        "description": "Returns the whole conversation as markdown: every message and note,\noldest first.",
        "operationId": "get_conversations_id_markdown",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation, as markdown.",
            "content": {
              "text/markdown": {
                "example": "# Broken widget\n\n**Jane Customer** <jane@customer.test>, 4 Mar 2026 08:00\n\nMy widget arrived broken.\n"
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/messages": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List a conversation's messages",
        "description": "Returns every message in a conversation, oldest first, one page at a\ntime. Each message is in the same form as `GET /api/messages/{key}`.",
        "operationId": "get_conversations_id_messages",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Results per page, 1 to 100. The default is 50.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `next_cursor` from the previous page.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "messages": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Message"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Send it back as `?cursor=` for the next page. `null` on the last."
                    }
                  },
                  "required": [
                    "messages",
                    "next_cursor"
                  ]
                },
                "example": {
                  "messages": [
                    {
                      "id": "k3f9zq",
                      "conversation_id": "broken-widget-k3f9zq",
                      "direction": "inbound",
                      "subject": "Broken widget",
                      "…": "…"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/bulk": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Change many conversations at once",
        "description": "Applies one action to many conversations at once. Either every\nconversation changes or none does. If any id is not a conversation the\nperson can see, the answer is `404` and nothing changes.\n\n| `action` | Effect | Also needs |\n|---|---|---|\n| `archive`, `unarchive` | Archives, or moves back to the inbox | |\n| `trash`, `untrash` | Moves to the trash, or restores | |\n| `spam`, `unspam` | Marks as spam, or not spam | |\n| `read`, `unread` | Marks read or unread for this person | |\n| `assign_me`, `unassign_me` | Assigns to this person, or unassigns | |\n| `label_add`, `label_remove` | Adds or removes a label | `label` |\n| `snooze` | Snoozes | `until` |\n\n`ids` can have up to 200 conversations. The answer has every row after\nthe change, in the order you asked.",
        "operationId": "post_conversations_bulk",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "archive",
                      "unarchive",
                      "trash",
                      "untrash",
                      "spam",
                      "unspam",
                      "read",
                      "unread",
                      "assign_me",
                      "unassign_me",
                      "label_add",
                      "label_remove",
                      "snooze"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Conversation ids, up to 200."
                  },
                  "label": {
                    "type": "string",
                    "description": "A label id, for `label_add` and `label_remove`."
                  },
                  "until": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the snooze ends, for `snooze`."
                  }
                },
                "required": [
                  "action",
                  "ids"
                ]
              },
              "example": {
                "action": "archive",
                "ids": [
                  "order-did-not-arrive-k3f9",
                  "refund-request-k8d2"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The conversations after the change, in the order given.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Conversation"
                      }
                    }
                  },
                  "required": [
                    "conversations"
                  ]
                },
                "example": {
                  "conversations": [
                    {
                      "id": "order-did-not-arrive-k3f9",
                      "status": "archived"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "An id is not a conversation this person can see. Nothing changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "An action it does not know, a missing label, a `snooze` without a future `until`, or more than 200 ids.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/archive": {
      "post": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Archive",
        "description": "Archives the conversation.",
        "operationId": "post_conversations_id_archive",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Unarchive",
        "description": "Moves the conversation back to the inbox.",
        "operationId": "delete_conversations_id_archive",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/trash": {
      "post": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Move to the Trash",
        "description": "Moves the conversation to the trash.",
        "operationId": "post_conversations_id_trash",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Restore from the Trash",
        "description": "Restores the conversation from the trash.",
        "operationId": "delete_conversations_id_trash",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/spam": {
      "post": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Mark as spam",
        "description": "Marks the conversation as spam.",
        "operationId": "post_conversations_id_spam",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Mark as not spam",
        "description": "Marks the conversation as not spam.",
        "operationId": "delete_conversations_id_spam",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/snooze": {
      "post": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Snooze",
        "description": "Snoozes the conversation until `until`, which must be in the future.",
        "operationId": "post_conversations_id_snooze",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "until": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the snooze ends. Must be in the future."
                  }
                },
                "required": [
                  "until"
                ]
              },
              "example": {
                "until": "2026-03-05T09:00:00Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No `until`, or one that is not in the future.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "End a snooze",
        "description": "Ends the snooze now.",
        "operationId": "delete_conversations_id_snooze",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/labels": {
      "post": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Add a label",
        "description": "Adds a label. Returns `201` if the label was added, or `200` if it was\nalready there.",
        "operationId": "post_conversations_id_labels",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label_id": {
                    "type": "string",
                    "description": "The label's id."
                  }
                },
                "required": [
                  "label_id"
                ]
              },
              "example": {
                "label_id": "billing"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The label was added.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "200": {
            "description": "The label was already there.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No `label_id`, or one that names no label.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/labels/{label}": {
      "delete": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Remove a label",
        "description": "Removes a label. Returns `200` even if the label was not there.",
        "operationId": "delete_conversations_id_labels_label",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "label",
            "in": "path",
            "description": "The label's id: its slug, which does not change when the label is renamed.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/assignments": {
      "post": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Assign",
        "description": "Assigns the conversation to a member, given by `member_id` or `email`.\nWith a personal token, `{\"member\": \"me\"}` assigns it to yourself.\nReturns `201` for a new assignment, or `200` if it already existed.",
        "operationId": "post_conversations_id_assignments",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "member_id": {
                    "type": "integer",
                    "description": "The member's id."
                  },
                  "email": {
                    "type": "string",
                    "description": "Or the member's email address."
                  },
                  "member": {
                    "type": "string",
                    "enum": [
                      "me"
                    ],
                    "description": "Or `me`, with a personal token."
                  }
                }
              },
              "example": {
                "member_id": 3
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Assigned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "200": {
            "description": "Already assigned to this member.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Nobody named, a member who is not active, or `me` from a team token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/assignments/{member}": {
      "delete": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Unassign",
        "description": "Unassigns a member, given by id, email, or `me` with a personal token.",
        "operationId": "delete_conversations_id_assignments_member",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "member",
            "in": "path",
            "description": "Who to take off: a member's id, their email address, or `me` (a person's own token only).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`me` from a team token, or nobody it can name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/mailboxes": {
      "patch": {
        "tags": [
          "Conversation actions"
        ],
        "summary": "Set mailboxes",
        "description": "Sets the mailboxes the conversation is in. A token can only add or\nremove its own mailboxes. Asking to change another mailbox returns\n`422`.",
        "operationId": "patch_conversations_id_mailboxes",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mailbox_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "description": "Every mailbox the conversation should be in."
                  }
                },
                "required": [
                  "mailbox_ids"
                ]
              },
              "example": {
                "mailbox_ids": [
                  1,
                  2
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A mailbox this caller may not move it into or out of, or none at all.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/read": {
      "post": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Mark read",
        "description": "Marks the conversation read, as opening it in the web app does. Also\nclears this person's notifications for it. `previous_seen_at` is when\nthe person last saw it, or `null`.",
        "operationId": "post_conversations_id_read",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "When this person last saw it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "previous_seen_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When this person saw it before now. `null` if never."
                    }
                  },
                  "required": [
                    "previous_seen_at"
                  ]
                },
                "example": {
                  "previous_seen_at": "2026-03-03T17:00:00Z"
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Mark unread",
        "description": "Marks the conversation unread.",
        "operationId": "delete_conversations_id_read",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/bookmark": {
      "post": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Bookmark",
        "description": "Bookmarks the conversation.",
        "operationId": "post_conversations_id_bookmark",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Remove bookmark",
        "description": "Removes the bookmark.",
        "operationId": "delete_conversations_id_bookmark",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/follow": {
      "post": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Follow",
        "description": "Follows the conversation, so this person is notified about it.",
        "operationId": "post_conversations_id_follow",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Stop following",
        "description": "Stops following the conversation.",
        "operationId": "delete_conversations_id_follow",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/aside": {
      "post": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Set aside",
        "description": "Sets the conversation aside: it is marked read and moves from this\nperson's Brand new to their Unassigned. Nobody else's inbox changes.\n\nOnly a conversation in this person's Brand new can be set aside.\nAnything else returns `409 not_brand_new`. Setting aside a conversation\nthat is already set aside returns `200` and changes nothing.",
        "operationId": "post_conversations_id_aside",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Not in this person's Brand new.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_brand_new",
                  "message": "Only a conversation in your Brand new can be set aside. This one is already somebody's, or no longer open."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Your own marks"
        ],
        "summary": "Undo set aside",
        "description": "Undoes setting the conversation aside.",
        "operationId": "delete_conversations_id_aside",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation after the change.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "conversation"
                  ]
                },
                "examples": {
                  "conversation": {
                    "$ref": "#/components/examples/conversation"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/notes": {
      "get": {
        "tags": [
          "Notes"
        ],
        "summary": "List notes",
        "description": "Lists a conversation's notes, oldest first. Each note includes its\n`reactions` and `deletable_until`. File links here need a token; the\nthread's file links are signed.",
        "operationId": "get_conversations_id_notes",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Results per page, 1 to 100. The default is 50.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `next_cursor` from the previous page.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "notes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Note"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Send it back as `?cursor=` for the next page. `null` on the last."
                    }
                  },
                  "required": [
                    "notes",
                    "next_cursor"
                  ]
                },
                "example": {
                  "notes": [
                    {
                      "id": "n8x2",
                      "conversation_id": "broken-widget-k3f9zq",
                      "html_body": "<p>Sending a new one</p>",
                      "text_body": "Sending a new one",
                      "created_at": "2026-03-04T08:10:00Z",
                      "edited_at": null,
                      "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq?focus=n-n8x2#n-n8x2",
                      "author": {
                        "type": "member",
                        "id": 3,
                        "name": "Sarah Chen"
                      },
                      "attachments": [],
                      "reactions": [
                        {
                          "id": 31,
                          "emoji": "👍",
                          "member": {
                            "type": "member",
                            "id": 4,
                            "name": "Bob"
                          },
                          "mine": false
                        }
                      ],
                      "deletable_until": null
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Notes"
        ],
        "summary": "Add a note",
        "description": "Writes a note. A note must have text.\n\nTo attach files, upload each one first with `POST /api/files` and list\ntheir ids in `attachments`. Or send `multipart/form-data` with a `body`\npart and one `attachments` part per file. Either way, a note can have\nup to 20 files, totalling 10 MB. More files returns `422`, and more\nbytes returns `413`. A file that is already\nattached returns `409`, and one this caller did not upload returns\n`404`. Either way, nothing is saved.",
        "operationId": "post_conversations_id_notes",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "The note, as HTML or plain text. `html_body` also works."
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids of files from `POST /files`. In a multipart request, one file part each instead. At most 20 files and 10 MB in total either way."
                  }
                },
                "required": [
                  "body"
                ]
              },
              "example": {
                "body": "<p>Rang her, the parcel is at the depot. @sarah can you chase?</p>"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The note.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "note": {
                      "$ref": "#/components/schemas/Note"
                    }
                  },
                  "required": [
                    "note"
                  ]
                },
                "example": {
                  "note": {
                    "id": "n8x2",
                    "conversation_id": "broken-widget-k3f9zq",
                    "html_body": "<p>…</p>",
                    "text_body": "…",
                    "created_at": "2026-03-04T09:20:00Z",
                    "edited_at": null,
                    "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq?focus=n-n8x2#n-n8x2",
                    "author": {
                      "type": "api_token",
                      "id": 7,
                      "name": "The night shift"
                    },
                    "attachments": [
                      {
                        "id": 12,
                        "filename": "screenshot.png",
                        "content_type": "image/png",
                        "byte_size": 48213,
                        "inline": false,
                        "url": "https://jelly.acme.test/api/attachments/12"
                      }
                    ],
                    "reactions": [],
                    "deletable_until": null
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see, or a file in `attachments` that this caller did not upload or that has expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A file from `POST /files` is already attached. Nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "already_attached",
                  "message": "That file is already attached to something. Upload it again to attach it twice"
                }
              }
            }
          },
          "413": {
            "description": "The files are over 10 MB in total. Nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_large",
                  "message": "Attachments must total under 10 MB"
                }
              }
            }
          },
          "422": {
            "description": "No words, more than 20 files, or `attachments` is not a list of distinct file ids.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/notes/{note}": {
      "delete": {
        "tags": [
          "Notes"
        ],
        "summary": "Delete a note",
        "description": "Deletes a note, with its files and reactions. Only the person who wrote\nit can delete it, and only for five minutes after posting.\n`deletable_until` says when that ends, or is `null` if this person\ncannot delete it.",
        "operationId": "delete_conversations_id_notes_note",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "note",
            "in": "path",
            "description": "The note's id, as the notes list and the thread hand it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted, with its files and reactions."
          },
          "403": {
            "description": "Someone else wrote the note.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "forbidden",
                  "message": "A note can be deleted by whoever wrote it, for a few minutes after posting"
                }
              }
            }
          },
          "404": {
            "description": "No such note.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "More than five minutes have passed since the note was posted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_late",
                  "message": "A note can be deleted by whoever wrote it, for a few minutes after posting"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/notes/{note}/reactions": {
      "post": {
        "tags": [
          "Notes"
        ],
        "summary": "Add a reaction",
        "description": "Adds a reaction to a note. The emoji must be one from\n`GET /api/reactions`. Reactions do not notify anyone.\n\nThe answer has the new reaction and all of the note's reactions. In\n`reactions`, `mine` is `true` for the reactions this person can change.",
        "operationId": "post_conversations_id_notes_note_reactions",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "note",
            "in": "path",
            "description": "The note's id, as the notes list and the thread hand it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emoji": {
                    "type": "string",
                    "description": "An emoji from `GET /api/reactions`."
                  }
                },
                "required": [
                  "emoji"
                ]
              },
              "example": {
                "emoji": "👍"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new reaction, and all of the note's reactions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "$ref": "#/components/schemas/Reaction"
                    },
                    "reactions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Reaction"
                      }
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions"
                  ]
                },
                "example": {
                  "reaction": {
                    "id": 33,
                    "emoji": "👍",
                    "member": {
                      "type": "member",
                      "id": 3,
                      "name": "Sarah Chen"
                    },
                    "mine": true
                  },
                  "reactions": [
                    {
                      "id": 31,
                      "emoji": "🎉",
                      "member": {
                        "type": "member",
                        "id": 4,
                        "name": "Bob"
                      },
                      "mine": false
                    },
                    {
                      "id": 33,
                      "emoji": "👍",
                      "member": {
                        "type": "member",
                        "id": 3,
                        "name": "Sarah Chen"
                      },
                      "mine": true
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such note.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Not an emoji from `GET /api/reactions`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": null,
                      "code": "invalid",
                      "message": "That's not a reaction Jelly knows about"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/notes/{note}/reactions/{reaction}": {
      "patch": {
        "tags": [
          "Notes"
        ],
        "summary": "Change a reaction",
        "description": "Changes one of your reactions to another emoji. The changed reaction\ngets a new `id` and moves to the end of the list.",
        "operationId": "patch_conversations_id_notes_note_reactions_reaction",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "note",
            "in": "path",
            "description": "The note's id, as the notes list and the thread hand it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reaction",
            "in": "path",
            "description": "The reaction's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emoji": {
                    "type": "string",
                    "description": "An emoji from `GET /api/reactions`."
                  }
                },
                "required": [
                  "emoji"
                ]
              },
              "example": {
                "emoji": "🎉"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The changed reaction, with a new id, and all of the note's reactions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reaction": {
                      "$ref": "#/components/schemas/Reaction"
                    },
                    "reactions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Reaction"
                      }
                    }
                  },
                  "required": [
                    "reaction",
                    "reactions"
                  ]
                },
                "example": {
                  "reaction": {
                    "id": 34,
                    "emoji": "🎉",
                    "member": {
                      "type": "member",
                      "id": 3,
                      "name": "Sarah Chen"
                    },
                    "mine": true
                  },
                  "reactions": [
                    {
                      "id": 34,
                      "emoji": "🎉",
                      "member": {
                        "type": "member",
                        "id": 3,
                        "name": "Sarah Chen"
                      },
                      "mine": true
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such reaction by this person.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Not an emoji from `GET /api/reactions`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Notes"
        ],
        "summary": "Remove a reaction",
        "description": "Removes one of your reactions. The answer has the note's remaining\nreactions.",
        "operationId": "delete_conversations_id_notes_note_reactions_reaction",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "note",
            "in": "path",
            "description": "The note's id, as the notes list and the thread hand it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "reaction",
            "in": "path",
            "description": "The reaction's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The note's remaining reactions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reactions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Reaction"
                      }
                    }
                  },
                  "required": [
                    "reactions"
                  ]
                },
                "example": {
                  "reactions": []
                }
              }
            }
          },
          "404": {
            "description": "No such reaction by this person.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/reactions": {
      "get": {
        "tags": [
          "Notes"
        ],
        "summary": "List reaction emoji",
        "description": "Lists every emoji a note can be reacted with, in groups, each with its\nname.",
        "operationId": "get_reactions",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "reactions": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "emoji": {
                                  "type": "string"
                                },
                                "name": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "emoji",
                                "name"
                              ]
                            }
                          }
                        },
                        "required": [
                          "name",
                          "reactions"
                        ]
                      }
                    }
                  },
                  "required": [
                    "groups"
                  ]
                },
                "example": {
                  "groups": [
                    {
                      "name": "Frequently used",
                      "reactions": [
                        {
                          "emoji": "👍",
                          "name": "thumbs up"
                        },
                        {
                          "emoji": "👎",
                          "name": "thumbs down"
                        }
                      ]
                    },
                    {
                      "name": "Smileys & people",
                      "reactions": [
                        {
                          "emoji": "😀",
                          "name": "grinning face"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/messages/{key}": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Get a message",
        "description": "Returns a message with its files, its headers, and the result of the\nsender checks:\n\n```json\n\"authentication\": { \"spf\": \"pass\", \"dkim\": \"pass\", \"dmarc\": \"pass\" }\n```\n\nEach check is `null` if the original message is not stored.\n\n### Delivery\n\nAn outbound message also has `delivery`: whether it arrived.\n\n```json\n\"delivery\": {\n  \"state\": \"bounced\", \"label\": \"Bounced\", \"confirmed\": 0, \"expected\": 1,\n  \"issues\": [{\n    \"state\": \"bounced\", \"label\": \"Bounced\", \"recipient\": \"nobody@customer.test\",\n    \"said\": \"It was refused permanently: as far as customer.test is concerned, nobody@customer.test is not an address that exists.\",\n    \"todo\": \"Check it for a typo and send again to the right one, or ask them for another address. Sending to this one again will bounce again.\",\n    \"detail\": \"smtp;550 5.1.1 User unknown\", \"at\": \"2026-03-04T09:12:00Z\"\n  }]\n}\n```\n\n`state` is one of `delivered`, `sent`, `partly_delivered`, `bounced`,\n`deferred`, `spam_complaint`, `blocked`, `unsubscribed`, `suppressed`,\n`auth_failed`, `rate_limited`, `quota_exceeded`, `provider_down`,\n`refused` or `unknown`.\n\nOn a [sandbox team](/manual/advanced/api#sandbox), nothing is sent. The\nstate is `sent` and the label is `Would have been sent`.\n\n`confirmed` is how many recipients the sending service confirmed, out of\n`expected`. Some services report nothing back; then `expected` is `0`\nand the state is `sent`.\n\n`issues` has one entry for each recipient with a problem. `said` and\n`todo` are written to be shown to a person. See\n[Did it arrive?](/manual/basics/delivery).",
        "operationId": "get_messages_key",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "The message's key, as a thread or a message list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "$ref": "#/components/schemas/Message"
                    }
                  },
                  "required": [
                    "message"
                  ]
                },
                "example": {
                  "message": {
                    "id": "k9x2",
                    "conversation_id": "broken-widget-k3f9zq",
                    "direction": "outbound",
                    "sender_type": "member",
                    "from": {
                      "name": "Sarah Chen",
                      "email": "support@acme.test"
                    },
                    "to": [
                      {
                        "name": "Jane Customer",
                        "email": "jane@customer.test"
                      }
                    ],
                    "cc": [],
                    "bcc": [],
                    "subject": "Re: Broken widget",
                    "snippet": "A new one is on its way",
                    "text_body": "A new one is on its way.",
                    "html_body": "<p>A new one is on its way.</p>",
                    "automatic": false,
                    "message_id": "<k9x2@acme.test>",
                    "in_reply_to": "<CA+abc@customer.test>",
                    "sent_at": "2026-03-04T09:12:00Z",
                    "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq?focus=m-k9x2#m-k9x2",
                    "attachments": [],
                    "headers": {
                      "Subject": "Re: Broken widget"
                    },
                    "authentication": {
                      "spf": null,
                      "dkim": null,
                      "dmarc": null
                    },
                    "delivery": {
                      "state": "delivered",
                      "label": "Delivered",
                      "confirmed": 1,
                      "expected": 1,
                      "issues": []
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such message, or not visible to this token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/messages": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Insert a message",
        "description": "Only for a team with this feature turned on. Otherwise the route answers `403 plan`.\n\nPuts a message into the team's mail, as if it had arrived by email.\nA team has it only when we have turned it on for them. Until then the\nroute answers `403` with the error `plan`.\n\nWithout `reply_to`, the message starts a new conversation. With\n`reply_to`, it is added to the conversation of the message with that\nkey, as a reply to it.\n\nIf `from` is one of the team's addresses, the message is outbound.\nOtherwise it is inbound, and `to` or `cc` must include one of the\nteam's addresses. Nothing is sent, either way. A team token and a\npersonal token can both insert outbound messages.\n\nAn inbound message is treated as new mail: rules run, followers are\nnotified, webhooks and chat posts go out, and the autoresponder may\nreply. A message from a blocked contact is accepted and handled as\ntheir mail is: it goes to spam.\n\nA new conversation starts in the default mailbox, or in `mailbox_id`.\nThe caller must be able to use that mailbox; if it cannot, the answer\nis `422`. A token that was not given the default mailbox must send\n`mailbox_id`. A rule may still move the conversation to a mailbox the\ncaller cannot see; then the answer's `message` is `null`.\n\n`fields` and `external_id` are set on the message's conversation before\nrules run, so a rule can match on them. They add no line to the\nconversation's timeline. `fields` is checked first: a key that is not a\nconversation field, or a value of the wrong type, is a `422`, and\nnothing is delivered. If another conversation has the `external_id`,\nthe answer is `409` and nothing is delivered. A reply into the\nconversation that has it is allowed.\n\n`contact` sets the message's contact: its `name`, its `external_id`\nand its contact `fields`. The contact is the sender of an inbound\nmessage, or the first recipient outside the team of an outbound one.\nIt is created if it does not exist. The values are set before rules\nrun. They are checked the same way: a wrong field is a `422`, and an\n`external_id` another contact has is a `409`. Nothing is delivered\nthen. A `null` or empty `external_id` leaves the contact's own as it\nis.\n\nTo attach files, upload each one first with `POST /api/files` and list\ntheir ids in `attachments`. They are added to the message as mail\nattachments. Up to 20 files, totalling 10 MB; more returns `422` or\n`413`. A file that is already attached returns `409`, and one this\ncaller did not upload returns `404`. Either way, nothing is delivered.\n\nSend an `Idempotency-Key` header to retry safely: a retry with the same\nkey gets the first answer again and makes no second conversation (see\nRetrying safely).\n\nA field that goes into a mail header (`from`, `to`, `cc`, `subject`,\n`reply_to`) must not hold a line break. The request is refused with a\n422 if one does.\n\n`sent_at` may be in the past; the message is placed at that time.\n\nThe message comes back with `source` set to `api`, and `inserted_by`\nnaming the token or member that put it in. Its `authentication` checks\nare `null`: no mail server checked it.",
        "operationId": "post_messages",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "from": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "`{\"name\": \"…\", \"email\": \"…\"}`. One of the team's own addresses makes the message outbound."
                  },
                  "to": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "description": "An array of addresses, or one comma-separated string. For an inbound message, `to` or `cc` must include one of the team's addresses."
                  },
                  "cc": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "subject": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string",
                    "description": "The body as plain text. `text`, `html` or both."
                  },
                  "html": {
                    "type": "string",
                    "description": "The body as HTML."
                  },
                  "reply_to": {
                    "type": "string",
                    "description": "The key of the message this replies to. Without it, the message starts a new conversation."
                  },
                  "fields": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Conversation field values, by key, set before rules run."
                  },
                  "external_id": {
                    "type": "string",
                    "description": "Your own id for the conversation, set before rules run."
                  },
                  "contact": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "`{\"name\", \"external_id\", \"fields\"}`, set on the message's contact before rules run. The contact is the sender of an inbound message, or the first recipient outside the team of an outbound one. It is created if it does not exist."
                  },
                  "mailbox_id": {
                    "type": "integer",
                    "description": "The mailbox a new conversation starts in. Defaults to the default mailbox. It must be one the caller can use. Not with `reply_to`."
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids of files from `POST /files`, at most 20 and 10 MB in total. They become the message's attachments."
                  },
                  "sent_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it was written, as RFC 3339. Defaults to now. A time in the future is taken as now."
                  }
                },
                "required": [
                  "from",
                  "to"
                ]
              },
              "example": {
                "from": {
                  "name": "Jane Customer",
                  "email": "jane@customer.test"
                },
                "to": [
                  "support@acme.test"
                ],
                "subject": "Broken widget",
                "text": "My widget arrived broken.",
                "external_id": "order-1234",
                "fields": {
                  "plan": "enterprise"
                },
                "contact": {
                  "name": "Jane Customer",
                  "external_id": "user-881",
                  "fields": {
                    "seats": 12
                  }
                },
                "attachments": [
                  "file_k3f9zq2m7xw4b8n5c6d2"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new message. `message` is `null` if it went to a mailbox this token cannot see.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "$ref": "#/components/schemas/Message"
                    }
                  },
                  "required": [
                    "message"
                  ]
                },
                "example": {
                  "message": {
                    "id": "p4t7",
                    "conversation_id": "broken-widget-k3f9zq",
                    "direction": "inbound",
                    "sender_type": "contact",
                    "from": {
                      "name": "Jane Customer",
                      "email": "jane@customer.test"
                    },
                    "to": [
                      {
                        "name": "",
                        "email": "support@acme.test"
                      }
                    ],
                    "cc": [],
                    "bcc": [],
                    "subject": "Broken widget",
                    "snippet": "My widget arrived broken.",
                    "text_body": "My widget arrived broken.",
                    "html_body": "<p>My widget arrived broken.</p>",
                    "automatic": false,
                    "message_id": "<5f0c2a9e1b7d4e3c8a6f9b21@demo.jam.invalid>",
                    "in_reply_to": "",
                    "sent_at": "2026-03-04T09:20:00Z",
                    "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq?focus=m-p4t7#m-p4t7",
                    "source": "api",
                    "inserted_by": {
                      "type": "api_token",
                      "id": 7,
                      "name": "Acme Console"
                    },
                    "attachments": [
                      {
                        "id": "a3",
                        "filename": "photo.jpg",
                        "…": "…"
                      }
                    ],
                    "headers": {
                      "Subject": "Broken widget"
                    },
                    "authentication": {
                      "spf": null,
                      "dkim": null,
                      "dmarc": null
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The team does not have this feature turned on.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "plan",
                  "message": "Putting messages in through the API is not turned on for your team."
                }
              }
            }
          },
          "404": {
            "description": "`reply_to` names no message this caller can see, or a file in `attachments` was not uploaded by this caller or has expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Another conversation has this `external_id`, another contact has `contact.external_id`, or a file in `attachments` is already attached. Nothing is delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The files are over 10 MB in total. Nothing is delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_large",
                  "message": "Attachments must total under 10 MB"
                }
              }
            }
          },
          "422": {
            "description": "A field is missing or cannot be read, a header field holds a line break, an inbound message names none of the team's addresses, a key in `fields` or `contact.fields` is not a field of that kind or holds a value of the wrong type, `mailbox_id` is not a mailbox the caller can use, a new conversation would start in the default mailbox and the caller cannot use it, or `attachments` is not a list of up to 20 distinct file ids. Nothing is delivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/draft_reply": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Start a reply",
        "description": "Starts a reply to the conversation. With a personal token, recipients,\nfrom address and body are filled in as in the web app, so `body` is\noptional. With a team token, `body` is required.\n\nWithout recipients, the reply goes to the conversation's `reply`\nrecipients (see `GET /api/conversations/{id}`). Set `reply_to` to a\nmessage's key to answer that message instead.\n\nA conversation can have only one draft. If it already has one, the\nanswer is `409` with that draft.\n\n`client_key` makes it safe to retry the request. Send any string of 1\nto 64 characters. A second request with the same key returns the draft\nthe first one made, with `200`. Only personal tokens can use\n`client_key`. The `Idempotency-Key` header does the same for any\nwrite and any token (see Retrying safely).",
        "operationId": "post_conversations_id_draft_reply",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "The body, as HTML. Required with a team token. `html_body` also works."
                  },
                  "subject": {
                    "type": "string"
                  },
                  "reply_to": {
                    "type": "string",
                    "description": "The key of the message to reply to. Defaults to the newest. `message_id` also works."
                  },
                  "to": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "description": "An array of addresses, or one comma-separated string."
                  },
                  "cc": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "bcc": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "client_key": {
                    "type": "string",
                    "description": "1 to 64 characters, personal tokens only. Repeating a request with the same key returns the same draft."
                  }
                }
              },
              "example": {
                "body": "<p>Thanks Jane, a new one is on its way.</p>",
                "client_key": "0F8E2C1A-7D3B-4C2E-9A61-5B0E8C7F1D24"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new draft.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "examples": {
                  "draft": {
                    "$ref": "#/components/examples/draft"
                  }
                }
              }
            }
          },
          "200": {
            "description": "The draft already made with this `client_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "examples": {
                  "draft": {
                    "$ref": "#/components/examples/draft"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The conversation already has a draft. The response contains it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A recipient that cannot be read, a `client_key` in use or from a team token, or no `body` from a team token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Start a new message or forward",
        "description": "Starts a new message. A team token must send `to` and `body`.\n\nTo forward a message, set `forward` to its key. The draft includes the\noriginal message and its attachments, and does not need `to`.\n\nSet `mailbox_id` to start the conversation in a mailbox other than the\ndefault. It must be one the caller can use, or the answer is `422`.\n\n`client_key` works as it does for `draft_reply`.",
        "operationId": "post_drafts",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "description": "An array of addresses, or one comma-separated string."
                  },
                  "cc": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "bcc": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "subject": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string",
                    "description": "The body, as HTML. Required with a team token. `html_body` also works."
                  },
                  "from_address_id": {
                    "type": "integer",
                    "description": "An address id from `GET /api/me`. `from`, the address itself, also works."
                  },
                  "mailbox_id": {
                    "type": "integer",
                    "description": "The mailbox the conversation starts in once sent. Defaults to the default mailbox. It must be one the caller can use."
                  },
                  "forward": {
                    "type": "string",
                    "description": "The key of a message to forward."
                  },
                  "client_key": {
                    "type": "string",
                    "description": "As on `draft_reply`."
                  }
                }
              },
              "example": {
                "to": [
                  "Jane Customer <jane@customer.test>"
                ],
                "subject": "About your order",
                "body": "<p>Hello Jane</p>",
                "from_address_id": 1
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new draft.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "examples": {
                  "draft": {
                    "$ref": "#/components/examples/draft"
                  }
                }
              }
            }
          },
          "200": {
            "description": "The draft already made with this `client_key`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "examples": {
                  "draft": {
                    "$ref": "#/components/examples/draft"
                  }
                }
              }
            }
          },
          "422": {
            "description": "No `to` or `body` from a team token, a recipient that cannot be read, an address nobody may send from, or a mailbox the caller cannot use.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts/{id}": {
      "get": {
        "tags": [
          "Drafts"
        ],
        "summary": "Get a draft",
        "description": "Returns a draft. Reading a draft does not hold it, so the answer has no\n`lock_token`.",
        "operationId": "get_drafts_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "examples": {
                  "draft": {
                    "$ref": "#/components/examples/draft"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such draft, or not visible to this token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Drafts"
        ],
        "summary": "Save a draft",
        "description": "Saves a draft. Fields you leave out are not changed.\n\nWith a personal token, saving holds the draft and returns a new\n`lock_token`. If someone else holds it, the answer is `423`. With a team\ntoken, the answer is `423` if anyone holds it.\n\nSend `if_updated_at` with the `updated_at` you last saw. If the draft\nhas changed since then, the answer is `409 stale` with the current\ndraft, and nothing is saved.",
        "operationId": "patch_drafts_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "to": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ],
                    "description": "An array of addresses, or one comma-separated string."
                  },
                  "cc": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "bcc": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string"
                      }
                    ]
                  },
                  "subject": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string",
                    "description": "The body, as HTML. `html_body` also works."
                  },
                  "from_address_id": {
                    "type": "integer",
                    "description": "An address id this person can send from."
                  },
                  "mailbox_id": {
                    "type": "integer",
                    "description": "As on `POST /api/drafts`. Only for a new message or a forward."
                  },
                  "if_updated_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "The draft's `updated_at` when you last saw it. If it has changed since, the answer is `409 stale`."
                  }
                }
              },
              "example": {
                "body": "<p>Hello</p>",
                "if_updated_at": "2026-03-04T09:12:00Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The draft. A personal token gets a new `lock_token`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "examples": {
                  "draft": {
                    "$ref": "#/components/examples/draft"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such draft. It may have been sent or discarded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The draft changed after `if_updated_at`. Nothing was saved. The response contains the current draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "stale",
                  "message": "This draft has changed since you last saw it",
                  "draft": {
                    "id": 41,
                    "html_body": "<p>Bob’s reply.</p>",
                    "updated_at": "2026-03-04T09:15:41Z",
                    "locked": false
                  },
                  "quoted": ""
                }
              }
            }
          },
          "422": {
            "description": "A recipient that cannot be read, an address this person may not send from, a mailbox the caller cannot use or a `mailbox_id` on a reply, or an `if_updated_at` that is not a time.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Drafts"
        ],
        "summary": "Discard a draft",
        "description": "Discards a draft and its files. Returns `423` if someone else is\nediting it.",
        "operationId": "delete_drafts_id",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lock_token": {
                    "type": "string",
                    "description": "The `lock_token` you got when you started holding the draft."
                  }
                }
              },
              "example": {
                "lock_token": "2BxI…"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Discarded."
          },
          "404": {
            "description": "No such draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts/{id}/heartbeat": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Keep holding a draft",
        "description": "Keeps holding a draft without saving it. Send it every minute while a\ncompose screen is open but not saving. Returns `409` if you do not hold\nthe draft.",
        "operationId": "post_drafts_id_heartbeat",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "You still hold the draft."
          },
          "404": {
            "description": "No such draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "You do not hold the draft. Save it with `PATCH` first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_editing",
                  "message": "You are not editing this draft"
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts/{id}/lock": {
      "delete": {
        "tags": [
          "Drafts"
        ],
        "summary": "Release a draft",
        "description": "Releases a draft you hold, so someone else can edit it straight away.\nSend it when the compose screen closes. Returns `409 not_editing` if the\n`lock_token` is not the current one.",
        "operationId": "delete_drafts_id_lock",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lock_token": {
                    "type": "string",
                    "description": "The `lock_token` you got when you started holding the draft."
                  }
                },
                "required": [
                  "lock_token"
                ]
              },
              "example": {
                "lock_token": "2BxI…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The draft, now not held by anyone.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "example": {
                  "draft": {
                    "id": 41,
                    "locked": false,
                    "editor": null,
                    "…": "…"
                  },
                  "quoted": ""
                }
              }
            }
          },
          "404": {
            "description": "No such draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The `lock_token` is not the current one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_editing",
                  "message": "You are not editing this draft"
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts/{id}/attachments": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Add a file to a draft",
        "description": "Adds a file to a draft, sent as `multipart/form-data` in a field named\n`file`. Or send a JSON body, `{\"file_id\": \"…\"}`, naming a file you\nuploaded with `POST /api/files`. You must hold the draft.\n\nA file that is already attached returns `409`. A file that another\nperson or a team token uploaded returns `404`.\n\nA draft can have up to 20 files, totalling 10 MB. Going over returns\n`422` or `413`. An empty file returns `422`.",
        "operationId": "post_drafts_id_attachments",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "One file. Or send a JSON body, `{\"file_id\": \"…\"}`, naming a file from `POST /files`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The added file.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attachment": {
                      "$ref": "#/components/schemas/Attachment"
                    }
                  },
                  "required": [
                    "attachment"
                  ]
                },
                "example": {
                  "attachment": {
                    "id": 5,
                    "filename": "invoice.pdf",
                    "content_type": "application/pdf",
                    "byte_size": 88213,
                    "url": "https://jelly.acme.test/api/draft_attachments/5/download?expires=1772615100&signature=…"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such draft, or a `file_id` this person did not upload or that has expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A file from `POST /files` is already attached. Nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "already_attached",
                  "message": "That file is already attached to something. Upload it again to attach it twice"
                }
              }
            }
          },
          "413": {
            "description": "The draft's files would be over 10 MB in total.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_large",
                  "message": "Attachments must total under 10 MB"
                }
              }
            }
          },
          "422": {
            "description": "No file, an empty one, or a twenty-first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts/{id}/attachments/{attachment}": {
      "delete": {
        "tags": [
          "Drafts"
        ],
        "summary": "Remove a file from a draft",
        "description": "Removes a file from a draft. You must hold the draft.",
        "operationId": "delete_drafts_id_attachments_attachment",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "attachment",
            "in": "path",
            "description": "The attachment's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "404": {
            "description": "No such draft or file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts/{id}/saved_reply_files": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Copy a saved reply's files onto a draft",
        "description": "Copies a saved reply's files onto the draft. The answer is the draft's\nfull list of files. Files already on the draft are not added twice. You\nmust hold the draft, and the draft's file limits apply.",
        "operationId": "post_drafts_id_saved_reply_files",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "saved_reply_id": {
                    "type": "integer",
                    "description": "A saved reply id from `GET /api/saved_replies`."
                  }
                },
                "required": [
                  "saved_reply_id"
                ]
              },
              "example": {
                "saved_reply_id": 4
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "All of the draft's files.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attachments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Attachment"
                      }
                    }
                  },
                  "required": [
                    "attachments"
                  ]
                },
                "example": {
                  "attachments": [
                    {
                      "id": 31,
                      "filename": "invoice.pdf",
                      "content_type": "application/pdf",
                      "byte_size": 88213,
                      "url": "https://…"
                    },
                    {
                      "id": 32,
                      "filename": "returns-form.pdf",
                      "content_type": "application/pdf",
                      "byte_size": 48213,
                      "url": "https://…"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No such draft.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "The files would be over 10 MB in total. None were added.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No such saved reply, or they would take the draft past twenty files. None was added.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/uploads": {
      "post": {
        "tags": [
          "Drafts"
        ],
        "summary": "Upload a picture",
        "description": "Uploads a picture for the text of a draft or note, sent as\n`multipart/form-data` in a field named `file`, up to 10 MB.\n\nPut `url` in the body. Use `display_url` to show the picture until\n`links_expire_at`; do not save it in the body. A file that is not a\npicture returns `415`; add it as an attachment instead.",
        "operationId": "post_uploads",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "One picture, up to 10 MB."
                  }
                },
                "required": [
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The `url` to put in the body, and a `display_url` to show it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "description": "The picture's address, `/uploads/{hash}`, to put in the body."
                    },
                    "name": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "description": "Its content type."
                    },
                    "size": {
                      "type": "integer",
                      "description": "In bytes."
                    },
                    "display_url": {
                      "type": "string",
                      "description": "A signed link to show it by."
                    },
                    "links_expire_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When `display_url` stops working."
                    }
                  },
                  "required": [
                    "url",
                    "name",
                    "type",
                    "size",
                    "display_url",
                    "links_expire_at"
                  ]
                },
                "example": {
                  "url": "/uploads/9f86d081884c7d65",
                  "name": "shot.png",
                  "type": "image/png",
                  "size": 48213,
                  "display_url": "https://jelly.acme.test/api/uploads/9f86d081884c7d65/download?expires=1772615100&signature=…",
                  "links_expire_at": "2026-03-04T09:05:00Z"
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_large",
                  "message": "That picture is over 10 MB"
                }
              }
            }
          },
          "415": {
            "description": "Not a picture. Add it to the draft as a file instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_an_image",
                  "message": "That isn't a picture. Send it as an attachment instead"
                }
              }
            }
          },
          "400": {
            "description": "No file.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "file is required"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/drafts/{id}/send": {
      "post": {
        "tags": [
          "Sending"
        ],
        "summary": "Send a draft",
        "description": "Sends a draft. Include the `lock_token` from holding the draft; without\nit, the answer is `423`.\n\nWithout `at`, the message waits for the team's undo window\n(`send_delay_seconds` in `GET /api/me`) and then goes. With `at`, it\ngoes at that time.\n\nWhile the message waits, the answer has the `draft`, `scheduled_for`\nand `undo_until`. Once it has gone, the answer has the `message`.\nSending a draft that has already gone returns `404`, so a retry never\nsends twice.\n\n`if_updated_at` works as it does for `PATCH /api/drafts/{id}`. A missing\nrecipient, subject or body returns `422` with a message to show the\nperson. So does a `from` address the person may not send from. The\ndraft is not changed.",
        "operationId": "post_drafts_id_send",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "send",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: send",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "lock_token": {
                    "type": "string",
                    "description": "The `lock_token` you got when you started holding the draft."
                  },
                  "at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When to send. Without it, the draft is sent after the undo window."
                  },
                  "archive": {
                    "type": "boolean",
                    "description": "Archive the conversation after sending. A draft that starts a conversation, a new message or a forward, is always archived once sent, whatever this says."
                  },
                  "if_updated_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "As for `PATCH /api/drafts/{id}`."
                  }
                },
                "required": [
                  "lock_token"
                ]
              },
              "example": {
                "lock_token": "2BxI…",
                "at": "2026-03-05T09:00:00Z",
                "archive": false,
                "if_updated_at": "2026-03-05T08:59:12Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The draft while it waits to be sent, or the message once sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Draft"
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "The draft while it waits to be sent. `null` once it has gone."
                    },
                    "message": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/Message"
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "The message, once sent. `null` while it waits."
                    },
                    "scheduled_for": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "When it will be sent."
                    },
                    "undo_until": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "Until when `DELETE /api/drafts/{id}/send` can stop it."
                    }
                  },
                  "required": [
                    "draft",
                    "message",
                    "scheduled_for",
                    "undo_until"
                  ]
                },
                "example": {
                  "draft": {
                    "id": 41,
                    "locked": false,
                    "…": "…"
                  },
                  "message": null,
                  "scheduled_for": "2026-03-05T12:00:05Z",
                  "undo_until": "2026-03-05T12:00:05Z"
                }
              }
            }
          },
          "404": {
            "description": "No such draft, or it has already been sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The draft changed after `if_updated_at`. Nothing was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "stale",
                  "message": "This draft has changed since you last saw it",
                  "draft": {
                    "id": 41,
                    "…": "…"
                  }
                }
              }
            }
          },
          "422": {
            "description": "No recipient, an address that will not parse, no subject, or no words. `errors` has the web's own sentence.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "423": {
            "description": "Missing or wrong `lock_token`, or the draft is already waiting to be sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Sending"
        ],
        "summary": "Cancel a send",
        "description": "Cancels a send, until `undo_until`. The draft comes back held by the\ncaller, with a new `lock_token`.\n\nSend `{\"keep_editing\": false}` to leave the draft not held by anyone\ninstead.\n\nReturns `409 too_late` if the message is being sent at that moment, and\n`404` once it has gone.",
        "operationId": "delete_drafts_id_send",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "send",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: send",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "keep_editing": {
                    "type": "boolean",
                    "description": "`false`: leave the draft not held by anyone. By default, the caller holds it."
                  }
                }
              },
              "example": {
                "keep_editing": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The draft.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/Draft"
                    },
                    "quoted": {
                      "type": "string",
                      "description": "The message being answered, quoted, as HTML to show below the draft. Empty for a new message."
                    }
                  },
                  "required": [
                    "draft"
                  ]
                },
                "examples": {
                  "draft": {
                    "$ref": "#/components/examples/draft"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such draft, or it has been sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Too late: it is being sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_late",
                  "message": "Too late — it had already gone"
                }
              }
            }
          },
          "423": {
            "description": "Someone else is editing the draft. The response says who.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "locked",
                  "message": "Sarah Chen is writing this draft right now",
                  "editor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/events": {
      "get": {
        "tags": [
          "Live"
        ],
        "summary": "Stream changes",
        "description": "A stream of Server-Sent Events that stays open. Each change sends an\n`event: change` whose data is the changed conversation's id. Empty data\nmeans the lists have changed. A `: ping` comment comes every 25 seconds.\nWhen you get an event, fetch what changed.\n\nThe stream ends with a `close` event when the token stops working:\n\n```\nevent: close\ndata: expired\n```\n\n- `expired`: the access token has expired or been refreshed. Reconnect\n  with the new token.\n- `revoked`: the person has been signed out. Refreshing will fail.",
        "operationId": "get_events",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "Server-Sent Events, until the token expires.",
            "content": {
              "text/event-stream": {
                "example": "retry: 3000\n\nevent: change\ndata: broken-widget-k3f9zq\n\n: ping\n\nevent: close\ndata: expired\n"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/presence": {
      "post": {
        "tags": [
          "Live"
        ],
        "summary": "Set presence",
        "description": "Says what this person is doing in a conversation, so others can see it:\n`viewing`, `replying`, or `commenting`, which is writing a note. Send it\nevery 15 seconds while the conversation is open; a status not repeated\nfor 30 seconds is dropped. Send `gone` when the person leaves.\n\nOthers see the change on `/api/events`, and in the thread's `presence`.",
        "operationId": "post_conversations_id_presence",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "viewing",
                      "replying",
                      "commenting",
                      "gone"
                    ],
                    "description": "`commenting` is writing a note."
                  }
                },
                "required": [
                  "status"
                ]
              },
              "example": {
                "status": "replying"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Saved."
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/devices": {
      "post": {
        "tags": [
          "Devices"
        ],
        "summary": "Register a device",
        "description": "Registers a phone or a Mac for notifications. The device then gets the\nsame notifications the person gets in the web app. Send `platform` as\n`ios` from the phone app and `macos` from the Mac app.\n\nThe answer has the device's `id`, and a `key_id` and `key` used to\nencrypt its notifications. Notifications are encrypted with AES-256-GCM,\nso Jelly's relay and Apple cannot read them.\n\nRegistering the same device again returns the same device. Registering a\ndevice that another sign-in registered replaces that registration.\n\nA device is removed when the person signs out, their sign-in is revoked,\nor they leave the team. Check `push` in `GET /api/me` first: when it is\n`false`, no notifications are sent.",
        "operationId": "post_devices",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "platform": {
                    "type": "string",
                    "enum": [
                      "ios",
                      "macos"
                    ],
                    "description": "`ios` for the phone app, `macos` for the Mac app."
                  },
                  "token": {
                    "type": "string",
                    "description": "The APNs device token, in hex."
                  },
                  "environment": {
                    "type": "string",
                    "enum": [
                      "sandbox",
                      "production"
                    ]
                  },
                  "bundle_id": {
                    "type": "string"
                  },
                  "app_version": {
                    "type": "string"
                  }
                },
                "required": [
                  "platform",
                  "token",
                  "environment",
                  "bundle_id"
                ]
              },
              "example": {
                "platform": "ios",
                "token": "a1b2c3…",
                "environment": "production",
                "bundle_id": "com.letsjelly.jelly",
                "app_version": "1.4"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The device, and the key its notifications are encrypted with.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "key_id": {
                      "type": "string",
                      "description": "The key's name, sent with each notification."
                    },
                    "key": {
                      "type": "string",
                      "description": "The key, base64. Shown once."
                    }
                  },
                  "required": [
                    "id",
                    "key_id",
                    "key"
                  ]
                },
                "example": {
                  "id": 7,
                  "key_id": "q0Zc3n1V8kS2m-Yd4TQxRw",
                  "key": "c2VjcmV0IGtleSBvZiAzMiByYW5kb20gYnl0ZXMhIQ=="
                }
              }
            }
          },
          "422": {
            "description": "Not a device: `errors` says what is missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/devices/{id}": {
      "delete": {
        "tags": [
          "Devices"
        ],
        "summary": "Remove a device",
        "description": "Removes a device. Returns `404` if the device is not this person's.",
        "operationId": "delete_devices_id",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The device's id, as registering it answered.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "404": {
            "description": "No such device for this person.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/labels": {
      "get": {
        "tags": [
          "Labels"
        ],
        "summary": "List labels",
        "description": "Lists every label, by name.",
        "operationId": "get_labels",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "labels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Label"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "labels",
                    "next_cursor"
                  ]
                },
                "example": {
                  "labels": [
                    {
                      "id": "billing",
                      "name": "Billing",
                      "colour": "lemon",
                      "created_at": "2026-01-02T10:00:00Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Labels"
        ],
        "summary": "Create a label",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nCreates a label. If a label with that name exists, the answer is `200`\nwith that label. Without a valid `colour`, a colour is chosen for you.",
        "operationId": "post_labels",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "colour": {
                    "type": "string",
                    "description": "A colour from the palette."
                  }
                },
                "required": [
                  "name"
                ]
              },
              "example": {
                "name": "Billing",
                "colour": "lemon"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "$ref": "#/components/schemas/Label"
                    }
                  },
                  "required": [
                    "label"
                  ]
                },
                "example": {
                  "label": {
                    "id": "billing",
                    "name": "Billing",
                    "colour": "lemon",
                    "created_at": "2026-03-04T09:00:00Z"
                  }
                }
              }
            }
          },
          "200": {
            "description": "A label with that name already exists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "$ref": "#/components/schemas/Label"
                    }
                  },
                  "required": [
                    "label"
                  ]
                },
                "example": {
                  "label": {
                    "id": "billing",
                    "name": "Billing",
                    "colour": "lemon",
                    "created_at": "2026-01-02T10:00:00Z"
                  }
                }
              }
            }
          },
          "422": {
            "description": "No `name`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/labels/{slug}": {
      "patch": {
        "tags": [
          "Labels"
        ],
        "summary": "Update a label",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nRenames a label or changes its colour. The label's id does not change.",
        "operationId": "patch_labels_slug",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "description": "The label's id: its slug, which does not change when the label is renamed.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "colour": {
                    "type": "string",
                    "description": "A colour from the palette."
                  }
                }
              },
              "example": {
                "name": "Invoices"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "$ref": "#/components/schemas/Label"
                    }
                  },
                  "required": [
                    "label"
                  ]
                },
                "example": {
                  "label": {
                    "id": "billing",
                    "name": "Invoices",
                    "colour": "lemon",
                    "created_at": "2026-01-02T10:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such label.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A colour not in the palette, or a name another label has.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Labels"
        ],
        "summary": "Delete a label",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nDeletes a label and removes it from every conversation.",
        "operationId": "delete_labels_slug",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "description": "The label's id: its slug, which does not change when the label is renamed.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted, and removed from every conversation."
          },
          "404": {
            "description": "No such label.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/members": {
      "get": {
        "tags": [
          "Team"
        ],
        "summary": "List members",
        "description": "Lists every active member.",
        "operationId": "get_members",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Member"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "members",
                    "next_cursor"
                  ]
                },
                "example": {
                  "members": [
                    {
                      "id": 3,
                      "name": "Sarah Chen",
                      "email": "sarah@acme.test",
                      "role": "admin",
                      "active": true,
                      "avatar": {
                        "url": null,
                        "initials": "SC",
                        "tint": {
                          "light": {
                            "background": "#5e35b1",
                            "text": "#ffffff"
                          },
                          "dark": {
                            "background": "#5e35b1",
                            "text": "#ffffff"
                          }
                        }
                      }
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/mailboxes": {
      "get": {
        "tags": [
          "Team"
        ],
        "summary": "List mailboxes",
        "description": "Lists the mailboxes the caller can see. For a team token, these are the\nmailboxes it was given.",
        "operationId": "get_mailboxes",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mailboxes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Mailbox"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "mailboxes",
                    "next_cursor"
                  ]
                },
                "example": {
                  "mailboxes": [
                    {
                      "id": 1,
                      "name": "Support",
                      "default": true
                    },
                    {
                      "id": 2,
                      "name": "Sales",
                      "default": false
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/mailboxes/{id}/members": {
      "get": {
        "tags": [
          "Team"
        ],
        "summary": "List a mailbox's members",
        "description": "Lists the active members of a mailbox. Every member is in the default\nmailbox.",
        "operationId": "get_mailboxes_id_members",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The mailbox's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Member"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "members",
                    "next_cursor"
                  ]
                },
                "example": {
                  "members": [
                    {
                      "id": 3,
                      "name": "Sarah Chen",
                      "email": "sarah@acme.test",
                      "role": "admin",
                      "active": true
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "404": {
            "description": "No such mailbox, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/saved_replies": {
      "get": {
        "tags": [
          "Saved replies"
        ],
        "summary": "List saved replies",
        "description": "Lists every saved reply, by name. To use one in a draft:\n\n1. Insert `html_body` into the draft's body at the cursor.\n2. If the draft has no subject, use the reply's `subject`.\n3. Save the draft with `PATCH /api/drafts/{id}`.\n4. If the reply has `attachments`, copy them with\n   `POST /api/drafts/{id}/saved_reply_files`.\n\n`inline_images` maps each picture in `html_body` to a link you can use\nto display it. The links expire after five minutes, so fetch the list\nagain each time you show it.",
        "operationId": "get_saved_replies",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "saved_replies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SavedReply"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "saved_replies",
                    "next_cursor"
                  ]
                },
                "example": {
                  "saved_replies": [
                    {
                      "id": 4,
                      "name": "Order tracking",
                      "subject": "Your order",
                      "html_body": "<p>Your tracking link is below.</p><p><img src=\"/uploads/9f86d081\" alt=\"map.png\" class=\"jam-inline-image\"></p>",
                      "text_body": "Your tracking link is below.",
                      "inline_images": {
                        "/uploads/9f86d081": "https://jelly.acme.test/api/uploads/9f86d081/download?expires=1772615100&signature=…"
                      },
                      "attachments": [
                        {
                          "id": 7,
                          "filename": "returns-form.pdf",
                          "content_type": "application/pdf",
                          "byte_size": 48213,
                          "url": "https://jelly.acme.test/api/saved_reply_files/7/download?expires=1772615100&signature=…"
                        }
                      ],
                      "created_at": "2026-01-02T10:00:00Z",
                      "updated_at": "2026-02-02T10:00:00Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Find a contact, or list contacts by a field",
        "description": "With `email`, finds the contact with that address.\n\nWith `external_id`, finds the contact with that external id.\n\nWith `field`, lists the contacts that have a value for that field,\noldest first, a page at a time. Add `value` to list only the contacts\nwith that value: `?field=plan&value=pro`. Text is matched whatever its\ncapitals. A date is `YYYY-MM-DD`, and a boolean is `true` or `false`.\n\n```json\n{\"contacts\": [{\"id\": \"jane-customer\", \"email\": \"jane@customer.test\", \"name\": \"Jane Customer\", \"fields\": {\"plan\": \"pro\"}, \"…\": \"…\"}], \"next_cursor\": null}\n```",
        "operationId": "get_contacts",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "email",
            "in": "query",
            "description": "Their email address. Finds one contact.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "external_id",
            "in": "query",
            "description": "Their external id. Finds one contact.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "field",
            "in": "query",
            "description": "A contact field's key. Lists the contacts with a value for it, instead of finding one.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "value",
            "in": "query",
            "description": "With `field`, only contacts whose value is this one.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Results per page, 1 to 100. The default is 50.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "The `next_cursor` from the previous page.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "With `email`, the contact under `contact`. With `field`, a page of contacts under `contacts`, and `next_cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact": {
                      "$ref": "#/components/schemas/Contact",
                      "description": "With `email` or `external_id`."
                    },
                    "contacts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Contact"
                      },
                      "description": "With `field`."
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "With `field`: as for any list."
                    }
                  }
                },
                "example": {
                  "contact": {
                    "id": "jane-customer",
                    "email": "jane@customer.test",
                    "name": "Jane Customer",
                    "note": "Prefers phone",
                    "links": {
                      "CRM": "https://crm.acme.test/jane"
                    },
                    "fields": {
                      "plan": "pro",
                      "renews_on": "2027-01-01"
                    },
                    "blocked": false,
                    "never_spam": false,
                    "created_at": "2026-01-02T10:00:00Z",
                    "updated_at": "2026-03-04T09:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No contact with that address or external id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "None of `email`, `external_id` and `field`, a `field` that is not a contact field, or a `value` that field could not hold.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Create or update a contact",
        "description": "Creates a contact, or updates the one with that email address. Returns\n`201` for a new contact and `200` for an existing one. Fields you leave\nout are not changed.\n\n`fields` sets field values by key: `{\"fields\": {\"plan\": \"pro\", \"seats\":\n12}}`. Keys you leave out keep their values, and `null` clears one. A key\nthat is not a contact field, or a value of the wrong type, is a `422`\nthat names the field, and nothing is changed.\n\n`external_id` sets the contact's external id. `null` or `\"\"` clears it.\nIf another contact has it, the answer is `409` and nothing is changed.",
        "operationId": "post_contacts",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  },
                  "links": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Links, as name and URL pairs."
                  },
                  "fields": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Field values, by the field's key. `null` clears one."
                  },
                  "external_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Your own id for this contact, up to 255 characters. `null` or `\"\"` clears it."
                  },
                  "blocked": {
                    "type": "boolean"
                  },
                  "never_spam": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "email"
                ]
              },
              "example": {
                "email": "jane@customer.test",
                "name": "Jane Customer",
                "note": "Prefers phone",
                "links": {
                  "Their site": "https://customer.test"
                },
                "fields": {
                  "plan": "pro",
                  "seats": 12
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "A new contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact": {
                      "$ref": "#/components/schemas/Contact"
                    }
                  },
                  "required": [
                    "contact"
                  ]
                }
              }
            }
          },
          "200": {
            "description": "The updated contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact": {
                      "$ref": "#/components/schemas/Contact"
                    }
                  },
                  "required": [
                    "contact"
                  ]
                }
              }
            }
          },
          "409": {
            "description": "Another contact has this `external_id`. Nothing is changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No `email`, or not an address. A key in `fields` that is not a contact field, or a value of the wrong type. Nothing is changed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/contacts/suggest": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Suggest addresses for a recipient field",
        "description": "Suggests addresses for someone typing a recipient. Use it to fill in a\n`to`, `cc` or `bcc` field. Jelly's web composer suggests the same\naddresses in the same order.\n\n`q` is what has been typed. A contact is suggested when `q` is part of\ntheir name or of one of their addresses, whatever its capitals. A `q`\nunder 2 characters returns an empty list.\n\nEach suggestion is one address, so a contact with two addresses can be\ntwo suggestions. Put `value` in the field: it is the name and the\naddress, quoted where needed.\n\nMatches at the start of a word or an address come first. Then come the\ncontacts you last had mail with, then the rest by name. Blocked\ncontacts are never suggested.",
        "operationId": "get_contacts_suggest",
        "security": [
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "app"
        },
        "x-badges": [
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "App",
            "color": "#757575",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "What has been typed: part of a name or an address. Under 2 characters, the list is empty.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Suggestions at most, 1 to 20. The default is 8.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "suggestions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AddressSuggestion"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "suggestions",
                    "next_cursor"
                  ]
                },
                "example": {
                  "suggestions": [
                    {
                      "contact_id": "ann-lee",
                      "name": "Ann Lee",
                      "email": "ann@lee.test",
                      "value": "Ann Lee <ann@lee.test>"
                    },
                    {
                      "contact_id": "ann-lee",
                      "name": "Ann Lee",
                      "email": "ann.lee@home.test",
                      "value": "Ann Lee <ann.lee@home.test>"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "422": {
            "description": "A `limit` that is not 1 to 20.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsPerson"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/contact_fields": {
      "get": {
        "tags": [
          "Contact fields"
        ],
        "summary": "List contact fields",
        "description": "Lists every contact field, in the order the contact pane shows them.",
        "operationId": "get_contact_fields",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact_fields": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactField"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "contact_fields",
                    "next_cursor"
                  ]
                },
                "example": {
                  "contact_fields": [
                    {
                      "key": "plan",
                      "label": "Plan",
                      "type": "choice",
                      "options": [
                        "free",
                        "pro"
                      ],
                      "position": 1,
                      "created_at": "2026-01-02T10:00:00Z",
                      "updated_at": "2026-01-02T10:00:00Z"
                    },
                    {
                      "key": "renews_on",
                      "label": "Renews on",
                      "type": "date",
                      "position": 2,
                      "created_at": "2026-01-02T10:00:00Z",
                      "updated_at": "2026-01-02T10:00:00Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Contact fields"
        ],
        "summary": "Create a contact field",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nCreates a field, after the others. The `type` cannot change later. A\n`choice` needs `options`.",
        "operationId": "post_contact_fields",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Lowercase letters, digits and underscores, starting with a letter. It cannot change."
                  },
                  "label": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "text",
                      "number",
                      "date",
                      "boolean",
                      "url",
                      "choice"
                    ],
                    "description": "It cannot change."
                  },
                  "options": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The choices, for a `choice`."
                  }
                },
                "required": [
                  "key",
                  "label",
                  "type"
                ]
              },
              "example": {
                "key": "plan",
                "label": "Plan",
                "type": "choice",
                "options": [
                  "free",
                  "pro"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact_field": {
                      "$ref": "#/components/schemas/ContactField"
                    }
                  },
                  "required": [
                    "contact_field"
                  ]
                },
                "example": {
                  "contact_field": {
                    "key": "plan",
                    "label": "Plan",
                    "type": "choice",
                    "options": [
                      "free",
                      "pro"
                    ],
                    "position": 3,
                    "created_at": "2026-03-04T09:00:00Z",
                    "updated_at": "2026-03-04T09:00:00Z"
                  }
                }
              }
            }
          },
          "422": {
            "description": "A missing or malformed `key`, a key another field has, no `label`, an unknown `type`, or a `choice` without options.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/contact_fields/{key}": {
      "patch": {
        "tags": [
          "Contact fields"
        ],
        "summary": "Update a contact field",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nChanges a field's `label`, its `position`, or a `choice`'s `options`.\nSend every option you want to keep. An option a contact holds cannot be\nleft out: set those contacts to another value first.",
        "operationId": "patch_contact_fields_key",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Identifies the contact_field.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "position": {
                    "type": "integer",
                    "description": "Where it is shown, from 1."
                  },
                  "options": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "A `choice`'s options, all of them."
                  }
                }
              },
              "example": {
                "label": "Plan name",
                "position": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "contact_field": {
                      "$ref": "#/components/schemas/ContactField"
                    }
                  },
                  "required": [
                    "contact_field"
                  ]
                },
                "example": {
                  "contact_field": {
                    "key": "plan",
                    "label": "Plan name",
                    "type": "choice",
                    "options": [
                      "free",
                      "pro"
                    ],
                    "position": 1,
                    "created_at": "2026-01-02T10:00:00Z",
                    "updated_at": "2026-03-04T09:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "An empty `label`, a changed `key` or `type`, or `options` that leave out one a contact holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Contact fields"
        ],
        "summary": "Delete a contact field",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nDeletes a field and every contact's value for it. The key can then be\nused for a new field.",
        "operationId": "delete_contact_fields_key",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Identifies the contact_field.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted, with every contact's value for it."
          },
          "404": {
            "description": "No such field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/card_sources": {
      "get": {
        "tags": [
          "Contact cards"
        ],
        "summary": "List card sources",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nLists the team's card sources, oldest first. Needs **change team\nsettings**. Each source carries its signing `secret`.",
        "operationId": "get_card_sources",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "card_sources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CardSource"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "card_sources",
                    "next_cursor"
                  ]
                },
                "example": {
                  "card_sources": [
                    {
                      "id": 2,
                      "name": "Acme billing",
                      "url": "https://billing.acme.test/jelly/card",
                      "secret": "3f6c…",
                      "timeout_seconds": 3,
                      "active": true,
                      "created_by": {
                        "type": "member",
                        "id": 3,
                        "name": "Sarah Chen",
                        "email": "sarah@acme.test",
                        "role": "admin",
                        "active": true
                      },
                      "created_at": "2026-03-01T09:00:00Z",
                      "updated_at": "2026-03-01T09:00:00Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Contact cards"
        ],
        "summary": "Add a card source",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nAdds a card source. Needs **change team settings**. Jelly makes the\nsigning secret; it is in the answer.\n\n`url` must be `https` and on the public internet. A source on a private\nnetwork, or on this machine, is refused, both here and each time Jelly\nasks it.",
        "operationId": "post_card_sources",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Up to 80 characters."
                  },
                  "url": {
                    "type": "string",
                    "description": "An https URL on the public internet."
                  },
                  "timeout_seconds": {
                    "type": "integer",
                    "description": "1, 2 or 3. The default is 3."
                  },
                  "active": {
                    "type": "boolean",
                    "description": "The default is `true`."
                  }
                },
                "required": [
                  "name",
                  "url"
                ]
              },
              "example": {
                "name": "Acme billing",
                "url": "https://billing.acme.test/jelly/card"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "card_source": {
                      "$ref": "#/components/schemas/CardSource"
                    }
                  },
                  "required": [
                    "card_source"
                  ]
                },
                "example": {
                  "card_source": {
                    "id": 2,
                    "name": "Acme billing",
                    "url": "https://billing.acme.test/jelly/card",
                    "secret": "3f6c…",
                    "timeout_seconds": 3,
                    "active": true,
                    "created_by": {
                      "type": "member",
                      "id": 3,
                      "name": "Sarah Chen",
                      "email": "sarah@acme.test",
                      "role": "admin",
                      "active": true
                    },
                    "created_at": "2026-03-01T09:00:00Z",
                    "updated_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Contact cards are not on this team's plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "plan",
                  "message": "Contact cards are not on your team's plan. The Royal plan includes them."
                }
              }
            }
          },
          "422": {
            "description": "No `name`, or a `url` that is not https or not on the public internet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/card_sources/{id}": {
      "patch": {
        "tags": [
          "Contact cards"
        ],
        "summary": "Update a card source",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nChanges a card source. Send only the fields to change. Needs **change\nteam settings**. Changing `url` does not change the secret.",
        "operationId": "patch_card_sources_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the card_source.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  },
                  "timeout_seconds": {
                    "type": "integer"
                  },
                  "active": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "active": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "card_source": {
                      "$ref": "#/components/schemas/CardSource"
                    }
                  },
                  "required": [
                    "card_source"
                  ]
                },
                "example": {
                  "card_source": {
                    "id": 2,
                    "name": "Acme billing",
                    "url": "https://billing.acme.test/jelly/card",
                    "secret": "3f6c…",
                    "timeout_seconds": 3,
                    "active": true,
                    "created_by": {
                      "type": "member",
                      "id": 3,
                      "name": "Sarah Chen",
                      "email": "sarah@acme.test",
                      "role": "admin",
                      "active": true
                    },
                    "created_at": "2026-03-01T09:00:00Z",
                    "updated_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Contact cards are not on this team's plan. Turning a source off is still allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such card source.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A field with the wrong value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Contact cards"
        ],
        "summary": "Remove a card source",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nRemoves a card source. Needs **change team settings**.",
        "operationId": "delete_card_sources_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the card_source.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Removed."
          },
          "404": {
            "description": "No such card source.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversations/{id}/cards": {
      "get": {
        "tags": [
          "Contact cards"
        ],
        "summary": "Get a conversation's contact cards",
        "description": "Asks every active card source about the conversation's contact, and\nanswers with one item per source, in the order they were added. An\nitem has `card` and `error`: `card` is the source's answer, as Jelly\nread it, or `null` with the reason in `error`. A source that answers\n`404` has nothing about the contact, and has no item.\n\nThe sources are asked at the same time, so this takes as long as the\nslowest source, up to three seconds. Answers are kept for the source's\n`ttl_seconds`, so asking again soon after may not ask the source.\n\nA conversation with no contact answers an empty `cards` list. A card\nis shown to anyone who can see the conversation.",
        "operationId": "get_conversations_id_cards",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The conversation's id: its slug, as every list hands it out.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cards": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ContactCard"
                      }
                    }
                  },
                  "required": [
                    "cards"
                  ]
                },
                "example": {
                  "cards": [
                    {
                      "source": {
                        "id": 2,
                        "name": "Acme billing"
                      },
                      "card": {
                        "title": "Acme billing",
                        "rows": [
                          {
                            "label": "Plan",
                            "value": "Pro",
                            "href": "https://admin.acme.test/c/42"
                          }
                        ],
                        "badges": [
                          {
                            "label": "Past due",
                            "tone": "bad"
                          }
                        ],
                        "links": [
                          {
                            "label": "Open in admin",
                            "href": "https://admin.acme.test/c/42"
                          }
                        ],
                        "ttl_seconds": 300
                      },
                      "error": null,
                      "fetched_at": "2026-03-04T09:20:00Z"
                    },
                    {
                      "source": {
                        "id": 3,
                        "name": "CRM"
                      },
                      "card": null,
                      "error": "The source took longer than 3 seconds to answer",
                      "fetched_at": null
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Contact cards are not on this team's plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one this caller can see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhooks",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nReturns every webhook the caller may manage, oldest first. The\n`secret` is not included.",
        "operationId": "get_webhooks",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Webhook"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "webhooks",
                    "next_cursor"
                  ]
                },
                "example": {
                  "webhooks": [
                    {
                      "id": 2,
                      "url": "https://ops.acme.test/jelly",
                      "events": [
                        "conversation.archived",
                        "note.added"
                      ],
                      "all_mailboxes": false,
                      "mailbox_ids": [
                        4
                      ],
                      "active": true,
                      "deactivated_at": null,
                      "basic_auth_username": "",
                      "has_basic_auth_password": false,
                      "consecutive_failures": 0,
                      "last_success_at": "2026-03-01T10:31:00Z",
                      "last_error": null,
                      "last_error_at": null,
                      "created_at": "2026-03-01T09:00:00Z",
                      "updated_at": "2026-03-01T09:00:00Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Add a webhook",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nAdds a webhook. Give `url`, `events`, and either `all_mailboxes: true`\nor `mailbox_ids`. The answer includes the signing `secret`. Keep it:\nit is shown only here and when it is rotated.",
        "operationId": "post_webhooks",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "An https URL."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The events to send: change kinds. Version `2026-10-09` also accepts the old names."
                  },
                  "all_mailboxes": {
                    "type": "boolean",
                    "description": "`true` to hear every mailbox, private ones and new ones included."
                  },
                  "mailbox_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "description": "The mailboxes to hear, instead of `all_mailboxes`."
                  },
                  "basic_auth_username": {
                    "type": "string"
                  },
                  "basic_auth_password": {
                    "type": "string"
                  },
                  "active": {
                    "type": "boolean",
                    "description": "`false` stops all sending. `true` turns it back on and clears the failure count."
                  }
                },
                "required": [
                  "url",
                  "events"
                ]
              },
              "example": {
                "url": "https://ops.acme.test/jelly",
                "events": [
                  "conversation.archived",
                  "note.added"
                ],
                "mailbox_ids": [
                  4
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The webhook, with its `secret`. This is the only time it is shown, apart from a rotation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  },
                  "required": [
                    "webhook"
                  ]
                },
                "example": {
                  "webhook": {
                    "id": 2,
                    "secret": "9b1e…",
                    "url": "https://ops.acme.test/jelly",
                    "events": [
                      "conversation.archived",
                      "note.added"
                    ],
                    "all_mailboxes": false,
                    "mailbox_ids": [
                      4
                    ],
                    "active": true,
                    "deactivated_at": null,
                    "basic_auth_username": "",
                    "has_basic_auth_password": false,
                    "consecutive_failures": 0,
                    "last_success_at": "2026-03-01T10:31:00Z",
                    "last_error": null,
                    "last_error_at": null,
                    "created_at": "2026-03-01T09:00:00Z",
                    "updated_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Webhooks are not on the team's plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A `url` that is not https, no `events` or an unknown one, or neither `all_mailboxes: true` nor `mailbox_ids`. A token that cannot see every mailbox cannot use `all_mailboxes`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/webhooks/{id}": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Get a webhook",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nReturns one webhook, without its `secret`.",
        "operationId": "get_webhooks_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the webhook.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  },
                  "required": [
                    "webhook"
                  ]
                },
                "example": {
                  "webhook": {
                    "id": 2,
                    "url": "https://ops.acme.test/jelly",
                    "events": [
                      "conversation.archived",
                      "note.added"
                    ],
                    "all_mailboxes": false,
                    "mailbox_ids": [
                      4
                    ],
                    "active": true,
                    "deactivated_at": null,
                    "basic_auth_username": "",
                    "has_basic_auth_password": false,
                    "consecutive_failures": 0,
                    "last_success_at": "2026-03-01T10:31:00Z",
                    "last_error": null,
                    "last_error_at": null,
                    "created_at": "2026-03-01T09:00:00Z",
                    "updated_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such webhook, or one that hears mailboxes this token cannot see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Update a webhook",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nChanges the fields you give and leaves the rest alone. Give\n`all_mailboxes` or `mailbox_ids` to change what it hears. An empty\n`basic_auth_password` removes the password. `active: true` turns a\nwebhook back on and resets its failure count.",
        "operationId": "patch_webhooks_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the webhook.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "An https URL."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The events to send: change kinds. Version `2026-10-09` also accepts the old names."
                  },
                  "all_mailboxes": {
                    "type": "boolean",
                    "description": "`true` to hear every mailbox, private ones and new ones included."
                  },
                  "mailbox_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "description": "The mailboxes to hear, instead of `all_mailboxes`."
                  },
                  "basic_auth_username": {
                    "type": "string"
                  },
                  "basic_auth_password": {
                    "type": "string"
                  },
                  "active": {
                    "type": "boolean",
                    "description": "`false` stops all sending. `true` turns it back on and clears the failure count."
                  }
                }
              },
              "example": {
                "events": [
                  "conversation.archived"
                ],
                "all_mailboxes": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  },
                  "required": [
                    "webhook"
                  ]
                },
                "example": {
                  "webhook": {
                    "id": 2,
                    "url": "https://ops.acme.test/jelly",
                    "events": [
                      "conversation.archived",
                      "note.added"
                    ],
                    "all_mailboxes": false,
                    "mailbox_ids": [
                      4
                    ],
                    "active": true,
                    "deactivated_at": null,
                    "basic_auth_username": "",
                    "has_basic_auth_password": false,
                    "consecutive_failures": 0,
                    "last_success_at": "2026-03-01T10:31:00Z",
                    "last_error": null,
                    "last_error_at": null,
                    "created_at": "2026-03-01T09:00:00Z",
                    "updated_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Webhooks are not on the team's plan. Turning one off is still allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such webhook, or one that hears mailboxes this token cannot see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A field with the wrong value.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Remove a webhook",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nRemoves the webhook, its deliveries and its waiting retries.",
        "operationId": "delete_webhooks_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the webhook.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Removed, with its deliveries."
          },
          "404": {
            "description": "No such webhook, or one that hears mailboxes this token cannot see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/webhooks/{id}/secret": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Rotate a webhook's secret",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nMakes a new signing secret and returns it. Requests are signed with the\nnew secret at once, so update your receiver first.",
        "operationId": "post_webhooks_id_secret",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the webhook.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The webhook, with its new `secret`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/Webhook"
                    }
                  },
                  "required": [
                    "webhook"
                  ]
                },
                "example": {
                  "webhook": {
                    "id": 2,
                    "secret": "9b1e…",
                    "url": "https://ops.acme.test/jelly",
                    "events": [
                      "conversation.archived",
                      "note.added"
                    ],
                    "all_mailboxes": false,
                    "mailbox_ids": [
                      4
                    ],
                    "active": true,
                    "deactivated_at": null,
                    "basic_auth_username": "",
                    "has_basic_auth_password": false,
                    "consecutive_failures": 0,
                    "last_success_at": "2026-03-01T10:31:00Z",
                    "last_error": null,
                    "last_error_at": null,
                    "created_at": "2026-03-01T09:00:00Z",
                    "updated_at": "2026-03-01T09:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such webhook, or one that hears mailboxes this token cannot see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/webhooks/{id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nSends the `ping` event now and waits for the answer. The answer is the\ntry, with `ok` and `status` from your URL. A test is never tried again\nand never counts towards switching the webhook off. It works on a\nwebhook that is off. Its `seq` is the newest change in the feed, a\nplace to start reading from.",
        "operationId": "post_webhooks_id_test",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the webhook.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The try. `ok` and `status` are what the URL answered.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  },
                  "required": [
                    "delivery"
                  ]
                },
                "example": {
                  "delivery": {
                    "id": 811,
                    "delivery_id": "5c2f0a9e1b7d3c4a6e8f0b12",
                    "event": "ping",
                    "seq": 4182,
                    "ok": true,
                    "status": 200,
                    "error": null,
                    "attempt": 1,
                    "at": "2026-03-01T10:40:00Z",
                    "retry_at": null,
                    "gave_up": false,
                    "manual": true
                  }
                }
              }
            }
          },
          "403": {
            "description": "Webhooks are not on the team's plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such webhook, or one that hears mailboxes this token cannot see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/webhooks/{id}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List a webhook's deliveries",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nReturns the webhook's tries from the last 30 days, newest first. Each\ntry of one delivery has the same `delivery_id`.",
        "operationId": "get_webhooks_id_deliveries",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the webhook.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "1 to 100. The default is 50.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "`next_cursor` from the page before.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Send it back as `?cursor=` for the next page. `null` on the last."
                    }
                  },
                  "required": [
                    "deliveries",
                    "next_cursor"
                  ]
                },
                "example": {
                  "deliveries": [
                    {
                      "id": 811,
                      "delivery_id": "5c2f0a9e1b7d3c4a6e8f0b12",
                      "event": "ping",
                      "seq": 4182,
                      "ok": true,
                      "status": 200,
                      "error": null,
                      "attempt": 1,
                      "at": "2026-03-01T10:40:00Z",
                      "retry_at": null,
                      "gave_up": false,
                      "manual": true
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "404": {
            "description": "No such webhook, or one that hears mailboxes this token cannot see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "A bad `limit` or `cursor`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/webhooks/{id}/deliveries/{delivery}/redeliver": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a delivery again",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nSends a delivery again now: the same body, re-signed, with the same\n`X-Jelly-Delivery`. The answer is the new try. If it succeeds, any\nretry still waiting for that delivery is cancelled. A redelivery never\ncounts towards switching the webhook off.",
        "operationId": "post_webhooks_id_deliveries_delivery_redeliver",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Identifies the webhook.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "delivery",
            "in": "path",
            "description": "The `delivery_id`, which is `X-Jelly-Delivery`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The new try.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  },
                  "required": [
                    "delivery"
                  ]
                },
                "example": {
                  "delivery": {
                    "id": 811,
                    "delivery_id": "5c2f0a9e1b7d3c4a6e8f0b12",
                    "event": "ping",
                    "seq": 4182,
                    "ok": true,
                    "status": 200,
                    "error": null,
                    "attempt": 1,
                    "at": "2026-03-01T10:40:00Z",
                    "retry_at": null,
                    "gave_up": false,
                    "manual": true
                  }
                }
              }
            }
          },
          "403": {
            "description": "Webhooks are not on the team's plan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such webhook, or no such delivery in the last 30 days.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversation_fields": {
      "get": {
        "tags": [
          "Conversation fields"
        ],
        "summary": "List conversation fields",
        "description": "Lists every conversation field, in order.",
        "operationId": "get_conversation_fields",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation_fields": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ConversationField"
                      }
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Always `null`: the list is returned in full."
                    }
                  },
                  "required": [
                    "conversation_fields",
                    "next_cursor"
                  ]
                },
                "example": {
                  "conversation_fields": [
                    {
                      "key": "plan",
                      "label": "Plan",
                      "type": "choice",
                      "options": [
                        "free",
                        "enterprise"
                      ],
                      "position": 1,
                      "created_at": "2026-01-02T10:00:00Z",
                      "updated_at": "2026-01-02T10:00:00Z"
                    },
                    {
                      "key": "order",
                      "label": "Order",
                      "type": "text",
                      "position": 2,
                      "created_at": "2026-01-02T10:00:00Z",
                      "updated_at": "2026-01-02T10:00:00Z"
                    }
                  ],
                  "next_cursor": null
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Conversation fields"
        ],
        "summary": "Create a conversation field",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nCreates a field, after the others. The `type` cannot change later. A\n`choice` needs `options`.",
        "operationId": "post_conversation_fields",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "Lowercase letters, digits and underscores, starting with a letter. It cannot change."
                  },
                  "label": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "text",
                      "number",
                      "date",
                      "boolean",
                      "url",
                      "choice"
                    ],
                    "description": "It cannot change."
                  },
                  "options": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The choices, for a `choice`."
                  }
                },
                "required": [
                  "key",
                  "label",
                  "type"
                ]
              },
              "example": {
                "key": "order",
                "label": "Order",
                "type": "text"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation_field": {
                      "$ref": "#/components/schemas/ConversationField"
                    }
                  },
                  "required": [
                    "conversation_field"
                  ]
                },
                "example": {
                  "conversation_field": {
                    "key": "order",
                    "label": "Order",
                    "type": "text",
                    "position": 3,
                    "created_at": "2026-03-04T09:00:00Z",
                    "updated_at": "2026-03-04T09:00:00Z"
                  }
                }
              }
            }
          },
          "422": {
            "description": "A missing or malformed `key`, a key another field has, no `label`, an unknown `type`, or a `choice` without options.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/conversation_fields/{key}": {
      "patch": {
        "tags": [
          "Conversation fields"
        ],
        "summary": "Update a conversation field",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nChanges a field's `label`, its `position`, or a `choice`'s `options`.\nSend every option you want to keep. An option a conversation holds\ncannot be left out: set those conversations to another value first.",
        "operationId": "patch_conversation_fields_key",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Identifies the conversation_field.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string"
                  },
                  "position": {
                    "type": "integer",
                    "description": "Where it is shown, from 1."
                  },
                  "options": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "A `choice`'s options, all of them."
                  }
                }
              },
              "example": {
                "label": "Order number",
                "position": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "conversation_field": {
                      "$ref": "#/components/schemas/ConversationField"
                    }
                  },
                  "required": [
                    "conversation_field"
                  ]
                },
                "example": {
                  "conversation_field": {
                    "key": "order",
                    "label": "Order number",
                    "type": "text",
                    "position": 1,
                    "created_at": "2026-01-02T10:00:00Z",
                    "updated_at": "2026-03-04T09:00:00Z"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "An empty `label`, a changed `key` or `type`, or `options` that leave out one a conversation holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "tags": [
          "Conversation fields"
        ],
        "summary": "Delete a conversation field",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nDeletes a field and every conversation's value for it. Rules that use\nit stop matching. The key can then be used for a new field.",
        "operationId": "delete_conversation_fields_key",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "description": "Identifies the conversation_field.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted, with every conversation's value for it."
          },
          "404": {
            "description": "No such field.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/autoresponder": {
      "get": {
        "tags": [
          "Autoresponder"
        ],
        "summary": "Get the autoresponder",
        "description": "Returns the default autoresponder.",
        "operationId": "get_autoresponder",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "autoresponder": {
                      "$ref": "#/components/schemas/Autoresponder"
                    }
                  },
                  "required": [
                    "autoresponder"
                  ]
                },
                "example": {
                  "autoresponder": {
                    "enabled": true,
                    "message": "<p>Thanks, we'll be in touch.</p>",
                    "first_message_only": true,
                    "mark_conversation_as_read": false
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "tags": [
          "Autoresponder"
        ],
        "summary": "Update the autoresponder",
        "description": "Needs **change team settings**: a team token with that permission, or a person who is an Admin or the Owner. Anyone else gets `403 forbidden`.\n\nChanges the default autoresponder. Fields you leave out are not changed.\n\n- `first_message_only`: answer only the first message in a conversation.\n- `mark_conversation_as_read`: mark the conversation read after\n  answering.\n- `message`: HTML, or plain text.",
        "operationId": "patch_autoresponder",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": true,
          "scope": "settings",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Change team settings",
            "color": "#b26a00",
            "position": "before"
          },
          {
            "name": "Scope: settings",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "message": {
                    "type": "string",
                    "description": "HTML, or plain text."
                  },
                  "first_message_only": {
                    "type": "boolean"
                  },
                  "mark_conversation_as_read": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "enabled": true,
                "message": "<p>Thanks, we'll be in touch.</p>"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "autoresponder": {
                      "$ref": "#/components/schemas/Autoresponder"
                    }
                  },
                  "required": [
                    "autoresponder"
                  ]
                },
                "example": {
                  "autoresponder": {
                    "enabled": true,
                    "message": "<p>Thanks, we'll be in touch.</p>",
                    "first_message_only": true,
                    "mark_conversation_as_read": false
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/NeedsTeamSettings"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyKeyReused"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/changes": {
      "get": {
        "tags": [
          "Changes"
        ],
        "summary": "List changes",
        "description": "Any token. Changes to conversations the caller cannot see are left out.\n\nReturns changes in the order they happened, oldest first. Each change\nhas a `seq`, a number that only grows.\n\nTo keep up:\n\n1. Call it without `since`. Keep `latest_seq` from the response.\n2. Call it again with `since` set to what you kept. Handle each change,\n   then keep `next_cursor`.\n3. Repeat step 2. If `has_more` is `true`, call again at once.\n\nAdd `wait=25` to step 2 to wait up to 25 seconds for a change when there\nis none yet. The response comes as soon as something happens. This\nreplaces polling: a client that waits makes about one request every 25\nseconds.\n\n`next_cursor` can be past the last change in the response. The feed\nleaves out changes the caller cannot see, and the cursor moves past\nthem too.\n\nChanges are kept for 30 days. A `since` older than that gets\n`410 cursor_expired`. So does a `since` that is not from this feed. Then\nfetch what you need from the lists, such as\n`GET /conversations?updated_since=`, and start again from `latest_seq`.\n\nA change says what happened and to what, not what it is now. Fetch the\nconversation, message, note or contact for its current state. Ids are\nthe ones the rest of the API uses. An id is `null` when the change has\nnone, or when the thing no longer exists. `actor` is who did it (see\n[People](#description/people)). `summary` holds a few ids and values\nthat depend on the kind, listed below. It never holds anybody's words.\n\nA team token sees changes in the mailboxes it was given, and changes\nto contacts. A change that moves a conversation between mailboxes is\nseen in both, so a token learns that a conversation left its mailboxes.\n\n### Kinds\n\n| Kind | What happened |\n| --- | --- |\n| `conversation.created` | A conversation was opened: by mail arriving, a message sent, an insertion or a split. |\n| `message.received` | A message arrived from outside. |\n| `message.sent` | A message went out from the team. `summary.automatic` is true for the autoresponder's reply. |\n| `message.forwarded` | A rule forwarded a message to an outside address. |\n| `message.unwrapped` | A forwarded message was shown as the message inside it. |\n| `message.rewrapped` | An unwrapped message was put back as it arrived. |\n| `message.delivery_updated` | A sent message's delivery changed: delivered, bounced, complained. |\n| `message.reported` | A member reported a message that shows wrongly. |\n| `message.report_withdrawn` | A member withdrew a report. |\n| `message.report_answered` | Support answered a report. |\n| `message.report_closed` | A report was closed. |\n| `note.added` | A note was added. |\n| `note.updated` | A note was edited. |\n| `note.deleted` | A note was deleted. |\n| `conversation.assigned` | A member was assigned. `summary.member_id` is who. |\n| `conversation.claimed` | A member assigned themselves. `summary.member_id` is who. |\n| `conversation.unassigned` | A member was unassigned. `summary.member_id` is who. |\n| `conversation.labelled` | A label was added. `summary.label_id` is which. |\n| `conversation.unlabelled` | A label was removed, or deleted (`summary.label_deleted`). `summary.label_id` is which. |\n| `conversation.archived` | The conversation was archived. |\n| `conversation.unarchived` | The conversation came back to the inbox from the archive. |\n| `conversation.snoozed` | The conversation was snoozed. `summary.until` is when it wakes. |\n| `conversation.unsnoozed` | The conversation woke from a snooze, or was woken. |\n| `conversation.spammed` | The conversation was marked as spam. |\n| `conversation.unspammed` | The conversation was marked as not spam. |\n| `conversation.trashed` | The conversation was moved to the trash. |\n| `conversation.restored` | The conversation came back from the trash. |\n| `conversation.vaulted` | The conversation was moved to the vault. A move of a whole date range from Settings has `summary.quiet` and sends no webhook. |\n| `conversation.unvaulted` | The conversation came back to the inbox from the vault. |\n| `conversation.ignored` | A member ignored the conversation. |\n| `conversation.followed` | A member started following the conversation. |\n| `conversation.unfollowed` | A member stopped following the conversation. |\n| `conversation.mailboxes_changed` | The conversation's mailboxes changed. `summary.from_mailbox_ids` and `summary.mailbox_ids` are before and after. |\n| `conversation.field_set` | A conversation field was set or cleared. `summary.key` is which. |\n| `conversation.renamed` | The conversation's subject was changed. |\n| `conversation.split` | This conversation was split from another. `summary.from_id` is which. |\n| `conversation.split_off` | Messages were split off into a new conversation. `summary.to_id` is which. |\n| `conversation.draft_taken_over` | A member took over editing a draft reply. |\n| `contact.created` | A contact was created. |\n| `contact.updated` | A contact's name, addresses, note or other details changed. |\n| `contact.merged` | Another contact was merged into this one. `summary.merged_id` is the one that went, which no longer exists. |\n| `contact.field_set` | A contact field was set or cleared. `summary.key` is which. |\n\nAny kind can also have `summary.quiet` set to `true`. That means\nnobody was told about the change: it came from an import or a blocked\nsender. Webhooks are not sent quiet changes.",
        "operationId": "get_changes",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "description": "The `next_cursor` or `latest_seq` of an earlier response. Without it, the response is the latest page.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "kinds",
            "in": "query",
            "description": "Only these kinds, comma-separated, such as `conversation.archived,message.received`.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mailbox_id",
            "in": "query",
            "description": "Only changes to conversations in this mailbox.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "wait",
            "in": "query",
            "description": "Seconds to wait for a change when there is none yet, 0 to 25. The default is 0.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Results per page, 1 to 100. The default is 50.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "changes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Change"
                      },
                      "description": "Oldest first."
                    },
                    "next_cursor": {
                      "type": "integer",
                      "description": "The `seq` to send as `since` next. When there are no changes, the `since` you sent."
                    },
                    "latest_seq": {
                      "type": "integer",
                      "description": "The newest change in the feed when this answer was made."
                    },
                    "has_more": {
                      "type": "boolean",
                      "description": "More changes are waiting: ask again at once with `since=next_cursor`."
                    }
                  },
                  "required": [
                    "changes",
                    "next_cursor",
                    "latest_seq",
                    "has_more"
                  ]
                },
                "example": {
                  "changes": [
                    {
                      "seq": 4812,
                      "at": "2026-03-04T09:20:00Z",
                      "kind": "conversation.assigned",
                      "conversation_id": "broken-widget-k3f9zq",
                      "message_id": null,
                      "note_id": null,
                      "contact_id": null,
                      "actor": {
                        "type": "api_token",
                        "id": 7,
                        "name": "The night shift"
                      },
                      "summary": {
                        "member_id": 3
                      }
                    }
                  ],
                  "next_cursor": 4812,
                  "latest_seq": 4812,
                  "has_more": false
                }
              }
            }
          },
          "410": {
            "description": "`since` is older than the oldest change kept, or is not from this feed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "cursor_expired",
                  "message": "That cursor is older than the oldest change kept, or not from this feed. Resync from the lists, then start again from latest_seq."
                }
              }
            }
          },
          "422": {
            "description": "A `since` that is not a number, a kind that does not exist, a `mailbox_id` the caller cannot see, or a `wait` out of range.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/statistics": {
      "get": {
        "tags": [
          "Statistics"
        ],
        "summary": "Get statistics",
        "description": "Any token can call it, including a Member's personal token. The figures count only the conversations the caller can see.\n\nReturns the same figures as\n[Statistics](/manual/manage/statistics), which explains how\neach one is counted.\n\nThe figures count only the conversations the caller can see. A team\ntoken counts the mailboxes it was given. A personal token counts the\nmailboxes its person is in, whatever their role. So the figures are the\nwhole team's only if the caller can see every mailbox, and two callers\ncan get different figures for the same period. The response does not\nsay whether any mailbox was left out.\n\n- `needs_attention`: open conversations waiting for a reply, longest\n  first. `waiting_over_24h` and `waiting_over_8h` mark the ones the\n  Statistics page highlights.\n- `members`: one entry per member, in id order. `away` is `null` if the\n  member is available.\n- `daily_volume`, `response_time_trend`, `awaiting_reply_trend`: one\n  entry per day, oldest first.\n- `response_time_trend`: each day's `median_sec` is the median first\n  response time over the three days ending that day, or `null` if there\n  were none.\n- `conversation_depth`: how many messages the period's conversations\n  have. The last bucket's `max_messages` is `null`.\n- `first_responses_counted`: how many conversations the median is based\n  on.\n\nConversations in spam or the trash are not counted.",
        "operationId": "get_statistics",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "period",
            "in": "query",
            "description": "Number of days, 7 to 90. The default is 30.",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "statistics": {
                      "$ref": "#/components/schemas/Statistics"
                    }
                  },
                  "required": [
                    "statistics"
                  ]
                },
                "example": {
                  "statistics": {
                    "period_days": 30,
                    "from": "2026-02-05T00:00:00Z",
                    "to": "2026-03-07T00:00:00Z",
                    "summary": {
                      "conversations_started": 214,
                      "conversations_open": 18,
                      "awaiting_reply": 4,
                      "first_response_median_sec": 5400,
                      "first_responses_counted": 198
                    },
                    "daily_volume": [
                      {
                        "date": "2026-03-06",
                        "inbound": 12,
                        "outbound": 9
                      }
                    ],
                    "needs_attention": [
                      {
                        "id": "where-is-my-order-k3f9zq",
                        "subject": "Where is my order?",
                        "waiting_seconds": 93600,
                        "waiting_over_24h": true,
                        "waiting_over_8h": true,
                        "url": "https://jelly.acme.test/conversations/where-is-my-order-k3f9zq"
                      }
                    ],
                    "members": [
                      {
                        "member": {
                          "type": "member",
                          "id": 3,
                          "name": "Sarah Chen"
                        },
                        "replies_sent": 41,
                        "notes_added": 12,
                        "conversations_archived": 30,
                        "away": null
                      }
                    ],
                    "response_time_trend": [
                      {
                        "date": "2026-03-06",
                        "median_sec": 4800,
                        "responses": 21
                      }
                    ],
                    "awaiting_reply_trend": [
                      {
                        "date": "2026-03-06",
                        "awaiting_reply": 4
                      }
                    ],
                    "conversation_depth": {
                      "conversations": 214,
                      "average_messages": 2.9,
                      "buckets": [
                        {
                          "label": "1 message",
                          "min_messages": 1,
                          "max_messages": 1,
                          "conversations": 38
                        },
                        {
                          "label": "2–3 messages",
                          "min_messages": 2,
                          "max_messages": 3,
                          "conversations": 121
                        },
                        {
                          "label": "4–6 messages",
                          "min_messages": 4,
                          "max_messages": 6,
                          "conversations": 41
                        },
                        {
                          "label": "7–10 messages",
                          "min_messages": 7,
                          "max_messages": 10,
                          "conversations": 10
                        },
                        {
                          "label": "More than 10",
                          "min_messages": 11,
                          "max_messages": null,
                          "conversations": 4
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "A `period` outside 7 to 90.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/inbound": {
      "get": {
        "tags": [
          "Inbound"
        ],
        "summary": "Get inbound email setup",
        "description": "With a personal token, the person must be an Admin or the Owner; anyone else gets `403 forbidden`. Any team token can call it.\n\nReturns the domain this Jelly receives email at, whether the inbound\nhook is set up, the sending domains, and every address with how it\nreceives email.\n\n- `relay_address`: the address an SES receipt rule must name for this\n  address. Empty for an address that does not receive through the relay.\n- `default_inbound_address`: the address the team forwards email to, if\n  one is set. A receipt rule must name it too.\n- `inbound`: how the address receives email. `gmail` means through a\n  Gmail connection, not the relay.\n- `provider`: the service the address sends through: `postmark`, `gmail`\n  or `smtp`.\n- `secret_set`: whether the hook's secret is set. The secret itself is\n  never returned.",
        "operationId": "get_inbound",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "origin": {
                      "type": "string",
                      "description": "This Jelly's address."
                    },
                    "receiving_domain": {
                      "type": "string"
                    },
                    "default_inbound_address": {
                      "type": "string"
                    },
                    "hook": {
                      "type": "object",
                      "properties": {
                        "path": {
                          "type": "string"
                        },
                        "configured": {
                          "type": "boolean"
                        },
                        "secret_set": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "path",
                        "configured",
                        "secret_set"
                      ]
                    },
                    "sending_domains": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "relay_addresses": {
                      "type": "integer",
                      "description": "How many addresses receive through the relay."
                    },
                    "addresses": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "email": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "inbound": {
                            "type": "string",
                            "description": "How mail to it arrives: `relay`, `gmail`, `imap`, … More values may be added."
                          },
                          "inbound_local": {
                            "type": "string"
                          },
                          "relay_address": {
                            "type": "string"
                          },
                          "verification": {
                            "type": "string"
                          },
                          "provider": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "email",
                          "name",
                          "inbound",
                          "inbound_local",
                          "relay_address",
                          "verification",
                          "provider"
                        ]
                      }
                    }
                  },
                  "required": [
                    "origin",
                    "receiving_domain",
                    "default_inbound_address",
                    "hook",
                    "sending_domains",
                    "relay_addresses",
                    "addresses"
                  ]
                },
                "example": {
                  "origin": "https://jelly.acme.test",
                  "receiving_domain": "in.jelly.acme.test",
                  "default_inbound_address": "jelly@in.jelly.acme.test",
                  "hook": {
                    "path": "/hooks/ses/inbound",
                    "configured": true,
                    "secret_set": true
                  },
                  "sending_domains": [
                    "acme.test"
                  ],
                  "relay_addresses": 1,
                  "addresses": [
                    {
                      "email": "support@acme.test",
                      "name": "Support",
                      "inbound": "relay",
                      "inbound_local": "support",
                      "relay_address": "support@in.jelly.acme.test",
                      "verification": "domain",
                      "provider": "postmark"
                    },
                    {
                      "email": "someone@gmail.test",
                      "name": "A connected mailbox",
                      "inbound": "gmail",
                      "inbound_local": "someone",
                      "relay_address": "",
                      "verification": "gmail",
                      "provider": "gmail"
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "A person who is not an Admin.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "forbidden",
                  "message": "Inbound routing is visible to Admins and the owner."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/files": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Upload a file",
        "description": "Uploads one file, sent as `multipart/form-data` in a field named\n`file`. Any type is accepted, up to 10 MB. An empty file returns `422`.\n\nThe answer's `id` is what you attach: in `attachments` on\n`POST /api/messages` and `POST /api/conversations/{id}/notes`, or as\n`file_id` on `POST /api/drafts/{id}/attachments`.\n\n- Only the caller who uploaded a file can attach it. Anyone else gets\n  `404`.\n- A file can be attached once. A second attempt returns `409`. To\n  attach the same file twice, upload it twice.\n- A file that is not attached by `expires_at`, 24 hours after the\n  upload, is deleted. Attaching it after that returns `404`.\n- A caller can have 100 files waiting to be attached. More returns\n  `429`.\n\n`content_type` is the type the upload declared, except for pictures,\nwhose type is read from the file itself. `filename` is the name the\nupload gave, without any folder, control characters or text-direction\nmarks, and cut to 200 bytes. The same rules apply to a file sent\ndirectly to a note or a draft.",
        "operationId": "post_files",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "write",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: write",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JellyVersion"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "One file of any type, up to 10 MB."
                  }
                },
                "required": [
                  "file"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The file, waiting to be attached.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "file": {
                      "$ref": "#/components/schemas/File"
                    }
                  },
                  "required": [
                    "file"
                  ]
                },
                "example": {
                  "file": {
                    "id": "file_k3f9zq2m7xw4b8n5c6d2",
                    "filename": "screenshot.png",
                    "content_type": "image/png",
                    "byte_size": 48213,
                    "expires_at": "2026-03-05T09:20:00Z"
                  }
                }
              }
            }
          },
          "413": {
            "description": "Over 10 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_large",
                  "message": "That file is over 10 MB"
                }
              }
            }
          },
          "422": {
            "description": "No file, an empty one, or a body that is not multipart.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "unprocessable",
                  "message": "The request could not be accepted.",
                  "errors": [
                    {
                      "field": "until",
                      "code": "invalid",
                      "message": "until must be in the future"
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "This caller already has 100 files waiting to be attached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_many_files",
                  "message": "100 uploaded files are waiting to be attached. Attach them, or wait for them to expire"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/IdempotencyInProgress"
          }
        }
      }
    },
    "/api/attachments/{id}": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Get a file link",
        "description": "Redirects to a signed link for a file, valid for five minutes. Returns\n`404` if the file is on a conversation the caller cannot see.",
        "operationId": "get_attachments_id",
        "security": [
          {
            "teamToken": []
          },
          {
            "personToken": []
          }
        ],
        "x-jam-auth": {
          "team_token": true,
          "personal_token": true,
          "signed_link": false,
          "needs_team_settings": false,
          "scope": "read",
          "tier": "integration"
        },
        "x-badges": [
          {
            "name": "Team token",
            "color": "#2e7d32",
            "position": "before"
          },
          {
            "name": "Personal token",
            "color": "#6a1b9a",
            "position": "before"
          },
          {
            "name": "Scope: read",
            "color": "#455a64",
            "position": "before"
          },
          {
            "name": "Integration",
            "color": "#1565c0",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The attachment's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/JellyVersion"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirects to a signed download link, valid for five minutes."
          },
          "404": {
            "description": "No such file, or not visible to this token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/attachments/{id}/download": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download a file",
        "description": "Downloads a message's or note's file. Valid for five minutes.",
        "operationId": "get_attachments_id_download",
        "security": [],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": false,
          "signed_link": true,
          "needs_team_settings": false,
          "scope": "",
          "tier": ""
        },
        "x-badges": [
          {
            "name": "Signed link",
            "color": "#5d4037",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The attachment's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expires",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "signature",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/octet-stream": {}
            }
          },
          "404": {
            "description": "No such file, or the signature is wrong.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The link has expired. Fetch a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "expired",
                  "message": "This link has expired."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/draft_attachments/{id}/download": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download a draft's file",
        "description": "Downloads a draft's file. Valid for five minutes.",
        "operationId": "get_draft_attachments_id_download",
        "security": [],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": false,
          "signed_link": true,
          "needs_team_settings": false,
          "scope": "",
          "tier": ""
        },
        "x-badges": [
          {
            "name": "Signed link",
            "color": "#5d4037",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The draft attachment's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expires",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "signature",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/octet-stream": {}
            }
          },
          "404": {
            "description": "No such file, or the signature is wrong.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The link has expired. Fetch a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "expired",
                  "message": "This link has expired."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/saved_reply_files/{id}/download": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download a saved reply's file",
        "description": "Downloads a saved reply's file. Valid for five minutes.",
        "operationId": "get_saved_reply_files_id_download",
        "security": [],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": false,
          "signed_link": true,
          "needs_team_settings": false,
          "scope": "",
          "tier": ""
        },
        "x-badges": [
          {
            "name": "Signed link",
            "color": "#5d4037",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The saved reply file's id, a number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expires",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "signature",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "application/octet-stream": {}
            }
          },
          "404": {
            "description": "No such file, or the signature is wrong.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The link has expired. Fetch a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "expired",
                  "message": "This link has expired."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/uploads/{hash}/download": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download an uploaded picture",
        "description": "A picture from the text of a note, reply or draft. Valid for five\nminutes.",
        "operationId": "get_uploads_hash_download",
        "security": [],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": false,
          "signed_link": true,
          "needs_team_settings": false,
          "scope": "",
          "tier": ""
        },
        "x-badges": [
          {
            "name": "Signed link",
            "color": "#5d4037",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "description": "The picture's hash, the last segment of its `/uploads/{hash}` address.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expires",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "signature",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "image/*": {}
            }
          },
          "404": {
            "description": "No such picture, or the signature is wrong.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The link has expired. Fetch a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "expired",
                  "message": "This link has expired."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/avatars/{hash}/download": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download an avatar",
        "description": "A member's or contact's picture. Valid for at least a day.",
        "operationId": "get_avatars_hash_download",
        "security": [],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": false,
          "signed_link": true,
          "needs_team_settings": false,
          "scope": "",
          "tier": ""
        },
        "x-badges": [
          {
            "name": "Signed link",
            "color": "#5d4037",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "description": "The picture's hash. Key an image cache by it rather than by the whole link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expires",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "signature",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "image/*": {}
            }
          },
          "404": {
            "description": "No such picture, or the signature is wrong.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The link has expired. Fetch a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "expired",
                  "message": "This link has expired."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/team/logo/{hash}/download": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Download the team's logo",
        "description": "",
        "operationId": "get_team_logo_hash_download",
        "security": [],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": false,
          "signed_link": true,
          "needs_team_settings": false,
          "scope": "",
          "tier": ""
        },
        "x-badges": [
          {
            "name": "Signed link",
            "color": "#5d4037",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "description": "Identifies the logo.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expires",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "signature",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "image/png": {}
            }
          },
          "404": {
            "description": "No such picture, or the signature is wrong.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The link has expired. Fetch a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "expired",
                  "message": "This link has expired."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/remote": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Fetch a remote picture",
        "description": "A picture from another website in a message, fetched by Jelly so the\nother site does not see the reader. Valid for an hour.",
        "operationId": "get_remote",
        "security": [],
        "x-jam-auth": {
          "team_token": false,
          "personal_token": false,
          "signed_link": true,
          "needs_team_settings": false,
          "scope": "",
          "tier": ""
        },
        "x-badges": [
          {
            "name": "Signed link",
            "color": "#5d4037",
            "position": "before"
          }
        ],
        "parameters": [
          {
            "name": "u",
            "in": "query",
            "description": "The picture's URL.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "expires",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "signature",
            "in": "query",
            "description": "From the signed link.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file.",
            "content": {
              "image/*": {}
            }
          },
          "404": {
            "description": "The signature is wrong, or the picture could not be fetched.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The link has expired. Fetch a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "expired",
                  "message": "This link has expired."
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "webhooks": {
    "conversation.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Conversation opened: see `conversation.created` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.created",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.created"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.created",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.assigned": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Assigned: see `conversation.assigned` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.assigned",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.assigned"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "member": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Member"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The member `summary.member_id` names."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.assigned",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "member": {
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.claimed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Claimed: see `conversation.claimed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.claimed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.claimed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "member": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Member"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The member `summary.member_id` names."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.claimed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "member": {
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.unassigned": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Unassigned: see `conversation.unassigned` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.unassigned",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.unassigned"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "member": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Member"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The member `summary.member_id` names."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.unassigned",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "member": {
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.labelled": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Label added: see `conversation.labelled` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.labelled",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.labelled"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.labelled",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.unlabelled": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Label removed: see `conversation.unlabelled` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.unlabelled",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.unlabelled"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.unlabelled",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.archived": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Archived: see `conversation.archived` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.archived",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.archived"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.archived",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.unarchived": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Unarchived: see `conversation.unarchived` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.unarchived",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.unarchived"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.unarchived",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.snoozed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Snoozed: see `conversation.snoozed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.snoozed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.snoozed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.snoozed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.unsnoozed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Woken from a snooze: see `conversation.unsnoozed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.unsnoozed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.unsnoozed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.unsnoozed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.spammed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Marked as spam: see `conversation.spammed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.spammed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.spammed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.spammed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.unspammed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Marked as not spam: see `conversation.unspammed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.unspammed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.unspammed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.unspammed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.trashed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Moved to the trash: see `conversation.trashed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.trashed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.trashed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.trashed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.restored": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Restored from the trash: see `conversation.restored` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.restored",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.restored"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.restored",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.vaulted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Moved to the vault: see `conversation.vaulted` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.vaulted",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.vaulted"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.vaulted",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.unvaulted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Moved out of the vault: see `conversation.unvaulted` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.unvaulted",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.unvaulted"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.unvaulted",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.ignored": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Ignored: see `conversation.ignored` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.ignored",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.ignored"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.ignored",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.followed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Followed: see `conversation.followed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.followed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.followed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.followed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.unfollowed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Unfollowed: see `conversation.unfollowed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.unfollowed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.unfollowed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.unfollowed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.mailboxes_changed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Mailboxes changed: see `conversation.mailboxes_changed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.mailboxes_changed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.mailboxes_changed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.mailboxes_changed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.field_set": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Field set: see `conversation.field_set` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.field_set",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.field_set"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.field_set",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.renamed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Renamed: see `conversation.renamed` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.renamed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.renamed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.renamed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.split": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Split from another: see `conversation.split` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.split",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.split"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.split",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.split_off": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Messages split off: see `conversation.split_off` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.split_off",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.split_off"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.split_off",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation.draft_taken_over": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Draft taken over: see `conversation.draft_taken_over` under Changes.",
        "description": "",
        "operationId": "webhook_conversation.draft_taken_over",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation.draft_taken_over"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation.draft_taken_over",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.received": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Message received: see `message.received` under Changes.",
        "description": "",
        "operationId": "webhook_message.received",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.received"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.received",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.sent": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Message sent: see `message.sent` under Changes.",
        "description": "",
        "operationId": "webhook_message.sent",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.sent"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.sent",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.forwarded": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Message forwarded by a rule: see `message.forwarded` under Changes.",
        "description": "",
        "operationId": "webhook_message.forwarded",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.forwarded"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.forwarded",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.unwrapped": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Forwarded message unwrapped: see `message.unwrapped` under Changes.",
        "description": "",
        "operationId": "webhook_message.unwrapped",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.unwrapped"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.unwrapped",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.rewrapped": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Unwrapped message put back: see `message.rewrapped` under Changes.",
        "description": "",
        "operationId": "webhook_message.rewrapped",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.rewrapped"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.rewrapped",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.reported": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Message reported: see `message.reported` under Changes.",
        "description": "",
        "operationId": "webhook_message.reported",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.reported"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.reported",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.report_withdrawn": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Report withdrawn: see `message.report_withdrawn` under Changes.",
        "description": "",
        "operationId": "webhook_message.report_withdrawn",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.report_withdrawn"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.report_withdrawn",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.report_answered": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Report answered: see `message.report_answered` under Changes.",
        "description": "",
        "operationId": "webhook_message.report_answered",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.report_answered"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.report_answered",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.report_closed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Report closed: see `message.report_closed` under Changes.",
        "description": "",
        "operationId": "webhook_message.report_closed",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.report_closed"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.report_closed",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "note.added": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Note added: see `note.added` under Changes.",
        "description": "",
        "operationId": "webhook_note.added",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "note.added"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "note": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "The note, as in `GET .../notes`, without `reactions` or `deletable_until`. `null` once it is deleted."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "note.added",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "note": {
                    "id": "n8x2",
                    "html_body": "<p>Sending a new one</p>"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "note.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Note edited: see `note.updated` under Changes.",
        "description": "",
        "operationId": "webhook_note.updated",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "note.updated"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "note": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "The note, as in `GET .../notes`, without `reactions` or `deletable_until`. `null` once it is deleted."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "note.updated",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "note": {
                    "id": "n8x2",
                    "html_body": "<p>Sending a new one</p>"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "note.deleted": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Note deleted: see `note.deleted` under Changes.",
        "description": "",
        "operationId": "webhook_note.deleted",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "note.deleted"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "note": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "The note, as in `GET .../notes`, without `reactions` or `deletable_until`. `null` once it is deleted."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "note.deleted",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "note": {
                    "id": "n8x2",
                    "html_body": "<p>Sending a new one</p>"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "contact.created": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Contact created: see `contact.created` under Changes.",
        "description": "",
        "operationId": "webhook_contact.created",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "contact.created"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "contact": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Contact"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The contact as it is now."
                      }
                    },
                    "required": [
                      "summary"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "contact.created",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "contact": {
                    "id": 12,
                    "name": "Ann Lee",
                    "email": "ann@customer.test"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "contact.updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Contact updated: see `contact.updated` under Changes.",
        "description": "",
        "operationId": "webhook_contact.updated",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "contact.updated"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "contact": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Contact"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The contact as it is now."
                      }
                    },
                    "required": [
                      "summary"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "contact.updated",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "contact": {
                    "id": 12,
                    "name": "Ann Lee",
                    "email": "ann@customer.test"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "contact.merged": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Contacts merged: see `contact.merged` under Changes.",
        "description": "",
        "operationId": "webhook_contact.merged",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "contact.merged"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "contact": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Contact"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The contact as it is now."
                      }
                    },
                    "required": [
                      "summary"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "contact.merged",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "contact": {
                    "id": 12,
                    "name": "Ann Lee",
                    "email": "ann@customer.test"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "contact.field_set": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Contact field set: see `contact.field_set` under Changes.",
        "description": "",
        "operationId": "webhook_contact.field_set",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "contact.field_set"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "contact": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Contact"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The contact as it is now."
                      }
                    },
                    "required": [
                      "summary"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "contact.field_set",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "contact": {
                    "id": 12,
                    "name": "Ann Lee",
                    "email": "ann@customer.test"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "message.delivery_updated": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delivery changed: see `message.delivery_updated` under Changes.",
        "description": "",
        "operationId": "webhook_message.delivery_updated",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "message.delivery_updated"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      },
                      "summary": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The change's summary, as in `GET /api/changes`: ids and states."
                      },
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "oneOf": [
                          {
                            "$ref": "#/components/schemas/Message"
                          },
                          {
                            "type": "null"
                          }
                        ],
                        "description": "The message, or `null` if it has gone."
                      }
                    },
                    "required": [
                      "summary",
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "message.delivery_updated",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "actor": {
                    "type": "member",
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "summary": {}
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "ping": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "A test, sent when asked for. Not a change.",
        "description": "Sent by a test. `data.webhook` is the webhook's `id`, `url` and\n`events`.",
        "operationId": "webhook_ping",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "ping"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "webhook": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The webhook's `id`, `url` and `events`."
                      }
                    },
                    "required": [
                      "webhook"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "ping",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "webhook": {
                    "id": 2,
                    "url": "https://example.test/jelly",
                    "events": [
                      "conversation.archived"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "new_message": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Deprecated: use `message.received` and `message.sent`.",
        "description": "Deprecated: subscribe to `message.received` and `message.sent`. A\nmessage arrived, or someone on the team sent one. `new_conversation`\nis `true` when the message started the conversation.",
        "operationId": "webhook_new_message",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "new_message"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "message": {
                        "$ref": "#/components/schemas/Message"
                      },
                      "new_conversation": {
                        "type": "boolean",
                        "description": "This message started the conversation."
                      }
                    },
                    "required": [
                      "conversation",
                      "message",
                      "new_conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "new_message",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "message": {
                    "id": "k3f9zq",
                    "direction": "inbound"
                  },
                  "new_conversation": true
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "assigned": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Deprecated: use `conversation.assigned` and `conversation.claimed`.",
        "description": "Deprecated: subscribe to `conversation.assigned` and\n`conversation.claimed`. `assigned_by` is a member, a token, or `null`\nwhen a rule did it.",
        "operationId": "webhook_assigned",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "assigned"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "assignee": {
                        "$ref": "#/components/schemas/Member"
                      },
                      "assigned_by": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "A member, a token, or `null` for Jelly."
                      }
                    },
                    "required": [
                      "conversation",
                      "assignee"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "assigned",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "assignee": {
                    "id": 3,
                    "name": "Sarah Chen"
                  },
                  "assigned_by": {
                    "type": "api_token",
                    "id": 7,
                    "name": "The night shift"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "note_added": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Deprecated: use `note.added`.",
        "description": "Deprecated: subscribe to `note.added`.",
        "operationId": "webhook_note_added",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "note_added"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "note": {
                        "type": "object",
                        "additionalProperties": true,
                        "description": "The note, as in `GET .../notes`, without `reactions` or `deletable_until`."
                      }
                    },
                    "required": [
                      "conversation",
                      "note"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "note_added",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "note": {
                    "id": "n8x2",
                    "html_body": "<p>Sending a new one</p>"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation_archived": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Deprecated: use `conversation.archived`.",
        "description": "Deprecated: subscribe to `conversation.archived`. `actor` is who did\nit.",
        "operationId": "webhook_conversation_archived",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation_archived"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      }
                    },
                    "required": [
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation_archived",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    },
    "conversation_unarchived": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Deprecated: use `conversation.unarchived`.",
        "description": "Deprecated: subscribe to `conversation.unarchived`. `actor` is who did\nit, or `null` if Jelly did.",
        "operationId": "webhook_conversation_unarchived",
        "security": [],
        "parameters": [
          {
            "name": "X-Jelly-Event",
            "in": "header",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Timestamp",
            "in": "header",
            "description": "Unix seconds, when this try was sent.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Signature",
            "in": "header",
            "description": "`sha256=` and the hex HMAC-SHA256 of the request body, using the webhook's signing secret.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Jelly-Delivery",
            "in": "header",
            "description": "The delivery's id, the same on every try of it.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event": {
                    "type": "string",
                    "enum": [
                      "conversation_unarchived"
                    ],
                    "description": "The event."
                  },
                  "seq": {
                    "type": "integer",
                    "description": "The change it is about, as `seq` in `GET /api/changes`. A larger `seq` happened later."
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When it happened."
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "conversation": {
                        "$ref": "#/components/schemas/Conversation",
                        "description": "The conversation as it is now. Only `id`, for a webhook that does not hear the mailbox the conversation moved to."
                      },
                      "actor": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "additionalProperties": true,
                        "description": "Who did it: a member, a token, or `null` for Jelly."
                      }
                    },
                    "required": [
                      "conversation"
                    ]
                  }
                },
                "required": [
                  "event",
                  "seq",
                  "created_at",
                  "data"
                ]
              },
              "example": {
                "event": "conversation_unarchived",
                "seq": 4182,
                "created_at": "2026-03-01T10:30:00Z",
                "data": {
                  "conversation": {
                    "id": "broken-widget-k3f9zq",
                    "subject": "Broken widget"
                  },
                  "actor": null
                }
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Received. Any other answer, or none within ten seconds, is a failure, and the delivery is tried again for up to 48 hours."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "teamToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "jelly_…",
        "description": "A team token, made by an Admin on Settings → Integrations. It acts in its own name and sees only its own mailboxes."
      },
      "personToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "jellyp_…",
        "description": "A person's own token, from OAuth (authorization code with PKCE; see /.well-known/oauth-authorization-server). It lasts an hour; refresh it at /oauth/token."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "1 to 255 characters. Send the same request again with the same key and Jelly does not repeat it: you get the first answer back. See Retrying safely.",
        "required": false,
        "schema": {
          "type": "string"
        }
      },
      "JellyVersion": {
        "name": "Jelly-Version",
        "in": "header",
        "description": "The dated version to answer as. Without it, the version the token or app is pinned to. See Versions.",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "2026-10-22",
            "2026-10-09"
          ]
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "No token, or the token does not work. An expired personal token also gets `WWW-Authenticate: Bearer error=\"invalid_token\"`: refresh it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NeedsPerson": {
        "description": "This needs a personal token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "needs_person",
              "message": "This route is for a person's own sign-in; an integration token has no access to it."
            }
          }
        }
      },
      "NeedsTeamSettings": {
        "description": "Needs change team settings, or an Admin.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "forbidden",
              "message": "This token may not change the team's settings. Turn on \"change team settings\" for it in Settings → API tokens."
            }
          }
        }
      },
      "IdempotencyInProgress": {
        "description": "A request with the same `Idempotency-Key` is still running. `Retry-After` says how long to wait.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "idempotency_in_progress",
              "message": "A request with this Idempotency-Key is still being processed. Try again shortly."
            }
          }
        }
      },
      "IdempotencyKeyReused": {
        "description": "The `Idempotency-Key` was already used for a different method, path or body.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "idempotency_key_reused",
              "message": "This Idempotency-Key was already used for a different request. Use a new key for a new request."
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests. `Retry-After` says how long to wait.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "A code to branch on: `not_found`, `unprocessable`, `needs_person`, `forbidden`, `locked`, `stale`, `conflict`, `too_late`, `too_large`, `expired`, `paused`, `rate_limited`, …"
          },
          "message": {
            "type": "string",
            "description": "A message you can show to a person."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Problem"
            },
            "description": "On a `422`, the problems with the request. Before version `2026-10-22`, each is its `message` alone, a string."
          },
          "ref": {
            "type": "string",
            "description": "On a `500`, identifies the error in the server's log."
          },
          "scope": {
            "type": "string",
            "description": "On a `403 insufficient_scope`, the scope the request needed."
          },
          "oldest": {
            "type": "string",
            "description": "On a `400 unknown_version`, the oldest supported version."
          },
          "newest": {
            "type": "string",
            "description": "On a `400 unknown_version`, the newest supported version."
          },
          "editor": {
            "type": "object",
            "additionalProperties": true,
            "description": "On a `423`, who is editing the draft."
          },
          "draft": {
            "type": "object",
            "additionalProperties": true,
            "description": "On a `409` about a draft, the current draft."
          },
          "quoted": {
            "type": "string",
            "description": "With `draft`: the quoted message, as in a draft response."
          }
        },
        "required": [
          "error",
          "message"
        ],
        "description": "An error."
      },
      "Problem": {
        "type": "object",
        "properties": {
          "field": {
            "type": [
              "string",
              "null"
            ],
            "description": "The request field it is about, such as `until` or `fields.plan`, or `null` when it is about no one field."
          },
          "code": {
            "type": "string",
            "description": "`required`, `unknown` (the value names nothing this token can use) or `invalid`. More codes may be added."
          },
          "message": {
            "type": "string",
            "description": "A sentence you can show to a person."
          }
        },
        "required": [
          "field",
          "code",
          "message"
        ],
        "description": "One problem with a request."
      },
      "Conversation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Its slug."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own id for it, set by `PATCH` or when a message is inserted. Always present beside `fields`; on other rows, only when set."
          },
          "fields": {
            "type": "object",
            "additionalProperties": true,
            "description": "Its field values, by key. A field with no value is left out. On a single conversation, and on rows with `include=fields`."
          },
          "subject": {
            "type": "string"
          },
          "thread_subject": {
            "type": "string",
            "description": "The subject of the messages, if the team renamed the conversation."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "archived",
              "snoozed",
              "spam",
              "trash",
              "vaulted"
            ]
          },
          "snoozed_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "message_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_activity_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_message_at": {
            "type": "string",
            "format": "date-time"
          },
          "has_draft": {
            "type": "boolean"
          },
          "has_attachments": {
            "type": "boolean",
            "description": "Whether its messages have files."
          },
          "url": {
            "type": "string",
            "description": "Its page in the web app."
          },
          "messages_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/messages`."
          },
          "notes_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/notes`."
          },
          "markdown_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/markdown`."
          },
          "thread_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/thread`. Only for a personal token, on a row of a list."
          },
          "labels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Label"
            }
          },
          "assignees": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            }
          },
          "mailboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Mailbox"
            }
          },
          "contact": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Contact"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who the conversation is with. In lists, only `id`, `name`, `email` and `avatar`."
          },
          "snippet": {
            "type": "string"
          },
          "latest_note": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "author": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Actor"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "text": {
                "type": "string",
                "description": "Its words, cut to a snippet's length."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "author",
              "text",
              "created_at"
            ],
            "description": "The newest note, if nothing newer has happened."
          },
          "last_from": {
            "$ref": "#/components/schemas/Address",
            "description": "Who sent the last message."
          },
          "last_from_me": {
            "type": "boolean",
            "description": "Whether the team sent the last message."
          },
          "stamp": {
            "type": "string",
            "description": "Changes when the conversation changes."
          },
          "match": {
            "type": "string",
            "description": "From a search with `include_matches=true`: the text that matched, as HTML with `<mark>` around the match."
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            },
            "description": "Its messages, oldest first. Only on a single conversation."
          },
          "reply_from": {
            "$ref": "#/components/schemas/SendingAddress",
            "description": "The address a reply is sent from. Only on a single conversation."
          },
          "reply": {
            "type": "object",
            "properties": {
              "message_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The key of the message a reply answers."
              },
              "to": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "cc": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "message_id",
              "to",
              "cc"
            ],
            "description": "Who a reply goes to, and which message it answers. Only on a single conversation."
          },
          "unread": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "read_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When this person last read it. Personal tokens only."
          },
          "bookmarked": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "following": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "ignored": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "mine": {
            "type": "boolean",
            "description": "Assigned to this person. Personal tokens only."
          },
          "claimable": {
            "type": "boolean",
            "description": "This person can take it with **I got this**. Personal tokens only."
          }
        },
        "required": [
          "id",
          "subject",
          "thread_subject",
          "status",
          "snoozed_until",
          "message_count",
          "created_at",
          "last_activity_at",
          "last_message_at",
          "has_draft",
          "has_attachments",
          "url",
          "messages_url",
          "notes_url",
          "markdown_url",
          "labels",
          "assignees",
          "mailboxes",
          "contact",
          "latest_note"
        ],
        "description": "A conversation. A personal token also gets `unread`, `read_at`, `bookmarked`, `following`, `ignored`, `mine` and `claimable`."
      },
      "ListRow": {
        "type": "object",
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Its slug, or `null` for a draft that does not belong to a conversation yet."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own id for it, set by `PATCH` or when a message is inserted. Always present beside `fields`; on other rows, only when set."
          },
          "fields": {
            "type": "object",
            "additionalProperties": true,
            "description": "Its field values, by key. A field with no value is left out. On a single conversation, and on rows with `include=fields`."
          },
          "subject": {
            "type": "string"
          },
          "thread_subject": {
            "type": "string",
            "description": "The subject of the messages, if the team renamed the conversation."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "archived",
              "snoozed",
              "spam",
              "trash",
              "vaulted"
            ]
          },
          "snoozed_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "message_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_activity_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_message_at": {
            "type": "string",
            "format": "date-time"
          },
          "has_draft": {
            "type": "boolean"
          },
          "has_attachments": {
            "type": "boolean",
            "description": "Whether its messages have files."
          },
          "url": {
            "type": "string",
            "description": "Its page in the web app."
          },
          "messages_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/messages`."
          },
          "notes_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/notes`."
          },
          "markdown_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/markdown`."
          },
          "thread_url": {
            "type": "string",
            "description": "`GET /api/conversations/{id}/thread`. Only for a personal token, on a row of a list."
          },
          "labels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Label"
            }
          },
          "assignees": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            }
          },
          "mailboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Mailbox"
            }
          },
          "contact": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Contact"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who the conversation is with. In lists, only `id`, `name`, `email` and `avatar`."
          },
          "snippet": {
            "type": "string"
          },
          "latest_note": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "author": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/Actor"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "text": {
                "type": "string",
                "description": "Its words, cut to a snippet's length."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "required": [
              "author",
              "text",
              "created_at"
            ],
            "description": "The newest note, if nothing newer has happened."
          },
          "last_from": {
            "$ref": "#/components/schemas/Address",
            "description": "Who sent the last message."
          },
          "last_from_me": {
            "type": "boolean",
            "description": "Whether the team sent the last message."
          },
          "stamp": {
            "type": "string",
            "description": "Changes when the conversation changes."
          },
          "match": {
            "type": "string",
            "description": "From a search with `include_matches=true`: the text that matched, as HTML with `<mark>` around the match."
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            },
            "description": "Its messages, oldest first. Only on a single conversation."
          },
          "reply_from": {
            "$ref": "#/components/schemas/SendingAddress",
            "description": "The address a reply is sent from. Only on a single conversation."
          },
          "reply": {
            "type": "object",
            "properties": {
              "message_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The key of the message a reply answers."
              },
              "to": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "cc": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "message_id",
              "to",
              "cc"
            ],
            "description": "Who a reply goes to, and which message it answers. Only on a single conversation."
          },
          "unread": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "read_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When this person last read it. Personal tokens only."
          },
          "bookmarked": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "following": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "ignored": {
            "type": "boolean",
            "description": "Personal tokens only."
          },
          "mine": {
            "type": "boolean",
            "description": "Assigned to this person. Personal tokens only."
          },
          "claimable": {
            "type": "boolean",
            "description": "This person can take it with **I got this**. Personal tokens only."
          },
          "draft": {
            "$ref": "#/components/schemas/DraftSummary",
            "description": "In the drafts and scheduled lists, the draft."
          }
        },
        "required": [
          "id",
          "subject",
          "last_activity_at",
          "has_draft",
          "has_attachments"
        ],
        "description": "A row of a list: a conversation, or a draft that has no conversation yet."
      },
      "InboxSection": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "`new`, `mine`, `unassigned` or `others`. More may be added."
          },
          "title": {
            "type": "string"
          },
          "total": {
            "type": "integer"
          },
          "unread": {
            "type": "integer"
          },
          "colour": {
            "type": "object",
            "properties": {
              "wash": {
                "type": "object",
                "properties": {
                  "light": {
                    "type": "string"
                  },
                  "dark": {
                    "type": "string"
                  }
                },
                "required": [
                  "light",
                  "dark"
                ]
              },
              "ink": {
                "type": "object",
                "properties": {
                  "light": {
                    "type": "string"
                  },
                  "dark": {
                    "type": "string"
                  }
                },
                "required": [
                  "light",
                  "dark"
                ]
              }
            },
            "required": [
              "wash",
              "ink"
            ],
            "description": "The section's colours, for light and dark."
          },
          "conversations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Conversation"
            }
          },
          "next": {
            "type": [
              "string",
              "null"
            ],
            "description": "The list with the rest of the section, when there is more."
          }
        },
        "required": [
          "key",
          "title",
          "total",
          "unread",
          "conversations",
          "next"
        ],
        "description": "A section of the inbox."
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Its key."
          },
          "conversation_id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "sender_type": {
            "type": "string",
            "enum": [
              "contact",
              "member",
              "system"
            ],
            "description": "Who sent it: someone outside the team, a member, or Jelly."
          },
          "from": {
            "$ref": "#/components/schemas/Address"
          },
          "to": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Address"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Address"
            }
          },
          "bcc": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Address"
            }
          },
          "subject": {
            "type": "string"
          },
          "snippet": {
            "type": "string"
          },
          "text_body": {
            "type": "string"
          },
          "html_body": {
            "type": "string",
            "description": "Cleaned HTML, as shown in the web app."
          },
          "automatic": {
            "type": "boolean",
            "description": "Sent automatically, such as an autoresponse or a bounce."
          },
          "message_id": {
            "type": "string",
            "description": "Its `Message-ID` header."
          },
          "in_reply_to": {
            "type": "string",
            "description": "Its `In-Reply-To` header."
          },
          "sent_at": {
            "type": "string",
            "format": "date-time"
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "url": {
            "type": "string"
          },
          "relay_email": {
            "type": "string",
            "description": "The relay address it arrived through, when it came by the relay."
          },
          "headers": {
            "type": "object",
            "additionalProperties": true,
            "description": "The message's headers. Only from `GET /api/messages/{key}`."
          },
          "authentication": {
            "type": "object",
            "properties": {
              "spf": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "dkim": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "dmarc": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "spf",
              "dkim",
              "dmarc"
            ],
            "description": "`spf`, `dkim` and `dmarc` results, each `null` when there is none. Only from `GET /api/messages/{key}`."
          },
          "delivery": {
            "type": "object",
            "properties": {
              "state": {
                "type": "string",
                "description": "A word for the outcome, such as `delivered`. More values may be added."
              },
              "label": {
                "type": "string",
                "description": "The outcome, as the web app words it."
              },
              "confirmed": {
                "type": "integer",
                "description": "Recipients a provider has confirmed."
              },
              "expected": {
                "type": "integer",
                "description": "Recipients a provider might confirm."
              },
              "issues": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "state": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "recipient": {
                      "type": "string"
                    },
                    "said": {
                      "type": "string",
                      "description": "What the receiving server said."
                    },
                    "at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "todo": {
                      "type": "string",
                      "description": "What to do about it."
                    },
                    "detail": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "state",
                    "label",
                    "recipient",
                    "said",
                    "at"
                  ]
                }
              }
            },
            "required": [
              "state",
              "label",
              "confirmed",
              "expected",
              "issues"
            ],
            "description": "For a sent message, whether it was delivered."
          },
          "source": {
            "type": "string",
            "enum": [
              "email",
              "api"
            ],
            "description": "`api` if it was put in with `POST /api/messages`; `email` otherwise."
          },
          "inserted_by": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who put it in: a member or a token. Only when `source` is `api`."
          }
        },
        "required": [
          "id",
          "conversation_id",
          "direction",
          "sender_type",
          "from",
          "to",
          "cc",
          "bcc",
          "subject",
          "snippet",
          "text_body",
          "html_body",
          "automatic",
          "message_id",
          "in_reply_to",
          "sent_at",
          "attachments",
          "url",
          "source"
        ],
        "description": "One message."
      },
      "Address": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Empty when the address came without one."
          },
          "email": {
            "type": "string"
          },
          "avatar": {
            "$ref": "#/components/schemas/Avatar",
            "description": "Not in a webhook's payload."
          }
        },
        "required": [
          "name",
          "email"
        ],
        "description": "An email address and the name with it."
      },
      "SendingAddress": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "address": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "from_name": {
            "type": "string",
            "description": "The name mail sent from it by this person carries."
          },
          "default": {
            "type": "boolean",
            "description": "On a draft that names no address: this is the one it would go from."
          }
        },
        "required": [
          "id",
          "address",
          "name"
        ],
        "description": "One of the team's addresses that mail can be sent from."
      },
      "Attachment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "filename": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "byte_size": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "description": "Where to get it: an API route that redirects to a signed link, or a signed link itself."
          },
          "inline": {
            "type": "boolean",
            "description": "A picture in the message's own layout, rather than a file. On a message's and a note's files."
          }
        },
        "required": [
          "id",
          "filename",
          "content_type",
          "byte_size",
          "url"
        ],
        "description": "A file on a message, a note, a draft or a saved reply."
      },
      "File": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Name it in `attachments` to attach it."
          },
          "filename": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "byte_size": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When it is thrown away if nothing attaches it."
          }
        },
        "required": [
          "id",
          "filename",
          "content_type",
          "byte_size",
          "expires_at"
        ],
        "description": "A file uploaded with `POST /api/files`, waiting to be attached."
      },
      "Actor": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "member",
              "api_token"
            ]
          },
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "A member's."
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ],
            "description": "A member's."
          },
          "active": {
            "type": "boolean",
            "description": "A member's."
          },
          "avatar": {
            "$ref": "#/components/schemas/Avatar",
            "description": "A member's. Not in a webhook's payload."
          },
          "via": {
            "type": "string",
            "description": "The assistant or registered app a member acted through, such as `Claude`, when there was one."
          }
        },
        "required": [
          "type",
          "id",
          "name"
        ],
        "description": "Who did something: a member, or a team token. Where it can be `null`, `null` means Jelly did it."
      },
      "Note": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Its key."
          },
          "conversation_id": {
            "type": "string"
          },
          "html_body": {
            "type": "string"
          },
          "text_body": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "edited_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "url": {
            "type": "string"
          },
          "author": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "reactions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reaction"
            },
            "description": "Not in a webhook's payload."
          },
          "deletable_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Until when its author may delete it. Not in a webhook's payload."
          }
        },
        "required": [
          "id",
          "conversation_id",
          "html_body",
          "text_body",
          "created_at",
          "edited_at",
          "url",
          "author",
          "attachments"
        ],
        "description": "A note: the team's own words on a conversation, never sent."
      },
      "Reaction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "emoji": {
            "type": "string"
          },
          "member": {
            "$ref": "#/components/schemas/Actor"
          },
          "mine": {
            "type": "boolean",
            "description": "Whether the person asking put it there."
          }
        },
        "required": [
          "id",
          "emoji",
          "member",
          "mine"
        ],
        "description": "An emoji a member put on a note."
      },
      "Draft": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "conversation_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "`null` for a new message or a forward, until it is sent."
          },
          "subject": {
            "type": "string"
          },
          "html_body": {
            "type": "string"
          },
          "inline_images": {
            "type": "object",
            "additionalProperties": true,
            "description": "Signed links to draw the body's pictures by, keyed by the `/uploads/{hash}` the body names them by."
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "bcc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "from": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/SendingAddress"
              },
              {
                "type": "null"
              }
            ],
            "description": "The address it goes from. `null` when nothing can send from the address it names."
          },
          "mailbox_id": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The mailbox a new message's conversation starts in. `null` for a reply."
          },
          "locked": {
            "type": "boolean",
            "description": "Somebody is editing it now."
          },
          "editor": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who is editing it, while `locked`."
          },
          "started_by": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          },
          "last_edited_by": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "scheduled_for": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it will be sent, once it is on its way."
          },
          "scheduled_by": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ],
            "description": "Whose send it is, once it is on its way."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "description": "Its conversation's page. Only on a reply."
          },
          "lock_token": {
            "type": "string",
            "description": "Personal tokens only: proves you hold the draft. Send it with `send`, `lock` and `DELETE`."
          },
          "client_key": {
            "type": "string",
            "description": "The `client_key` it was made with, for the person who made it."
          }
        },
        "required": [
          "id",
          "conversation_id",
          "subject",
          "html_body",
          "inline_images",
          "to",
          "cc",
          "bcc",
          "from",
          "mailbox_id",
          "locked",
          "editor",
          "started_by",
          "last_edited_by",
          "attachments",
          "scheduled_for",
          "scheduled_by",
          "created_at",
          "updated_at"
        ],
        "description": "A reply or a new message being written."
      },
      "DraftSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "conversation_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "subject": {
            "type": "string"
          },
          "to": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "locked": {
            "type": "boolean"
          },
          "url": {
            "type": "string",
            "description": "Where to open it in the web app."
          },
          "scheduled_for": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "scheduled_by": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "conversation_id",
          "subject",
          "to",
          "cc",
          "updated_at",
          "locked",
          "url",
          "scheduled_for",
          "scheduled_by"
        ],
        "description": "A draft, as a row of the drafts or scheduled list shows it."
      },
      "ThreadConversation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "archived",
              "snoozed",
              "spam",
              "trash",
              "vaulted"
            ]
          },
          "labels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Label"
            }
          },
          "assignees": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            }
          },
          "followers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            }
          },
          "mailboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Mailbox"
            }
          },
          "contact": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Contact"
              },
              {
                "type": "null"
              }
            ]
          },
          "following": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          },
          "reply_from": {
            "$ref": "#/components/schemas/SendingAddress"
          },
          "reply": {
            "type": "object",
            "properties": {
              "message_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "to": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "cc": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "message_id",
              "to",
              "cc"
            ]
          }
        },
        "required": [
          "id",
          "subject",
          "status",
          "labels",
          "assignees",
          "followers",
          "mailboxes",
          "contact",
          "following",
          "url",
          "reply_from",
          "reply"
        ],
        "description": "The conversation at the head of a thread."
      },
      "ThreadItem": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "message",
              "note",
              "activity",
              "draft",
              "scheduled"
            ]
          },
          "id": {
            "type": "string",
            "description": "A message's or a note's key."
          },
          "draft_id": {
            "type": "integer",
            "description": "A draft's or a scheduled send's id."
          },
          "from": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Who a message is from, or the address a draft goes from."
          },
          "to": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Address"
            }
          },
          "cc": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Address"
            }
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "subject": {
            "type": "string"
          },
          "sent_at": {
            "type": "string",
            "format": "date-time"
          },
          "at": {
            "type": "string",
            "format": "date-time",
            "description": "When a note was written, or an activity happened."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "html": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "inline_images": {
            "type": "object",
            "additionalProperties": true
          },
          "remote_images_blocked": {
            "type": "integer",
            "description": "How many pictures from other websites were held back."
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            }
          },
          "url": {
            "type": "string"
          },
          "author": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          },
          "reactions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Reaction"
            }
          },
          "deletable_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "verb": {
            "type": "string",
            "description": "What an activity was, such as `assigned`."
          },
          "line": {
            "type": "string",
            "description": "An activity, as a sentence of HTML."
          },
          "actor": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          },
          "target": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Member"
              },
              {
                "type": "null"
              }
            ]
          },
          "locked": {
            "type": "boolean"
          },
          "editor": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          },
          "scheduled_for": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "undo_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "scheduled_by": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "kind"
        ],
        "description": "One item of a thread, as the web app draws it. `kind` says which fields it has: a `message`, a `note`, an `activity` line, the `draft` being written, or a `scheduled` send. More kinds may be added: skip one you do not know."
      },
      "Change": {
        "type": "object",
        "properties": {
          "seq": {
            "type": "integer",
            "description": "Its place in the feed. A larger `seq` happened later."
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "description": "What happened, such as `conversation.archived`. More kinds may be added."
          },
          "conversation_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "message_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "note_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "contact_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "actor": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Actor"
              },
              {
                "type": "null"
              }
            ]
          },
          "summary": {
            "type": "object",
            "additionalProperties": true,
            "description": "Ids and states that say what changed, by kind, such as `member_id` or `label_id`."
          }
        },
        "required": [
          "seq",
          "at",
          "kind",
          "conversation_id",
          "message_id",
          "note_id",
          "contact_id",
          "actor",
          "summary"
        ],
        "description": "One change to the team's mail, from `GET /api/changes`."
      },
      "Label": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Its slug. It stays the same when the label is renamed."
          },
          "name": {
            "type": "string"
          },
          "colour": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "colour",
          "created_at"
        ],
        "description": "A label."
      },
      "Mailbox": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "default": {
            "type": "boolean",
            "description": "Every member is in the default mailbox."
          }
        },
        "required": [
          "id",
          "name",
          "default"
        ],
        "description": "A mailbox. Mailboxes decide which conversations members and tokens can see."
      },
      "Member": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string",
            "description": "Their email address, if they have not set a name."
          },
          "email": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "member"
            ]
          },
          "active": {
            "type": "boolean",
            "description": "Not on `GET /api/me`, whose member is always active."
          },
          "avatar": {
            "$ref": "#/components/schemas/Avatar",
            "description": "Not in a webhook's payload."
          },
          "type": {
            "type": "string",
            "enum": [
              "member"
            ],
            "description": "Where a member stands for who did something."
          }
        },
        "required": [
          "id",
          "name",
          "email",
          "role"
        ],
        "description": "A member of the team."
      },
      "Avatar": {
        "type": "object",
        "properties": {
          "url": {
            "type": [
              "string",
              "null"
            ],
            "description": "A signed link to their picture, or `null` to show initials."
          },
          "initials": {
            "type": "string"
          },
          "tint": {
            "type": "object",
            "properties": {
              "light": {
                "type": "object",
                "properties": {
                  "background": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  }
                },
                "required": [
                  "background",
                  "text"
                ]
              },
              "dark": {
                "type": "object",
                "properties": {
                  "background": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  }
                },
                "required": [
                  "background",
                  "text"
                ]
              }
            },
            "required": [
              "light",
              "dark"
            ],
            "description": "Colours for initials, for light and dark."
          }
        },
        "required": [
          "url",
          "initials",
          "tint"
        ],
        "description": "How to show a person's picture."
      },
      "Contact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Its slug."
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own id for them, set by `POST /api/contacts`. Always present on a contact asked for on its own; on a conversation's contact, only when set."
          },
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "links": {
            "type": "object",
            "additionalProperties": true,
            "description": "Links, as name and URL pairs."
          },
          "fields": {
            "type": "object",
            "additionalProperties": true,
            "description": "The contact's field values, by key: a string, a number, a boolean, or a date as `YYYY-MM-DD`. A field with no value is left out. Not on a conversation's contact."
          },
          "blocked": {
            "type": "boolean"
          },
          "never_spam": {
            "type": "boolean"
          },
          "avatar": {
            "$ref": "#/components/schemas/Avatar",
            "description": "Not in a webhook's payload."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "email",
          "name"
        ],
        "description": "A person the team writes to."
      },
      "AddressSuggestion": {
        "type": "object",
        "properties": {
          "contact_id": {
            "type": "string",
            "description": "The contact's id."
          },
          "name": {
            "type": "string",
            "description": "The contact's name. Empty when they have none."
          },
          "email": {
            "type": "string"
          },
          "value": {
            "type": "string",
            "description": "The recipient to write in `to`, `cc` or `bcc`: the name and the address, with the name quoted when it needs to be."
          }
        },
        "required": [
          "contact_id",
          "name",
          "email",
          "value"
        ],
        "description": "An address to offer while someone types a recipient."
      },
      "ContactField": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Its id, in `fields` and in search. It never changes."
          },
          "label": {
            "type": "string",
            "description": "Its name, as people read it."
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "number",
              "date",
              "boolean",
              "url",
              "choice"
            ]
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Only for a `choice`."
          },
          "position": {
            "type": "integer",
            "description": "Where it is shown, from 1."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "key",
          "label",
          "type",
          "position",
          "created_at",
          "updated_at"
        ],
        "description": "A field the team keeps on every contact."
      },
      "CardSource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "secret": {
            "type": "string",
            "description": "Signs each request in `X-Jelly-Signature`."
          },
          "timeout_seconds": {
            "type": "integer"
          },
          "active": {
            "type": "boolean"
          },
          "created_by": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Member"
              },
              {
                "type": "null"
              }
            ],
            "description": "Who added it, or `null` when a token did."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "url",
          "secret",
          "timeout_seconds",
          "active",
          "created_by",
          "created_at",
          "updated_at"
        ],
        "description": "One of the team's systems that Jelly asks for contact cards."
      },
      "ContactCard": {
        "type": "object",
        "properties": {
          "source": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ]
          },
          "card": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "title": {
                "type": "string"
              },
              "rows": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "value": {
                      "type": "string"
                    },
                    "href": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "label",
                    "value"
                  ]
                }
              },
              "badges": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "tone": {
                      "type": "string",
                      "description": "`ok`, `warn` or `bad`."
                    }
                  },
                  "required": [
                    "label"
                  ]
                }
              },
              "links": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "href": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "label",
                    "href"
                  ]
                }
              },
              "ttl_seconds": {
                "type": "integer",
                "description": "How long Jelly keeps it."
              }
            },
            "required": [
              "title",
              "rows",
              "badges",
              "links",
              "ttl_seconds"
            ],
            "description": "The card, or `null` when the source did not give one."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why there is no card, in words to show."
          },
          "fetched_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "source",
          "card",
          "error",
          "fetched_at"
        ],
        "description": "One card source's answer about a conversation's contact."
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "all_mailboxes": {
            "type": "boolean",
            "description": "Whether it hears every mailbox."
          },
          "mailbox_ids": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "The mailboxes it hears, when not all of them."
          },
          "active": {
            "type": "boolean"
          },
          "deactivated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When Jelly turned it off after repeated failures."
          },
          "basic_auth_username": {
            "type": "string"
          },
          "has_basic_auth_password": {
            "type": "boolean"
          },
          "consecutive_failures": {
            "type": "integer"
          },
          "last_success_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_error_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "secret": {
            "type": "string",
            "description": "Only when it is made or rotated."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "all_mailboxes",
          "mailbox_ids",
          "active",
          "deactivated_at",
          "basic_auth_username",
          "has_basic_auth_password",
          "consecutive_failures",
          "last_success_at",
          "last_error",
          "last_error_at",
          "created_at",
          "updated_at"
        ],
        "description": "A URL Jelly sends events to."
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "delivery_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "`X-Jelly-Delivery`, the same on every try of one delivery."
          },
          "event": {
            "type": "string"
          },
          "seq": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The change it is about, as in `GET /api/changes`."
          },
          "ok": {
            "type": "boolean"
          },
          "status": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The HTTP status the URL answered, or `null` for no answer."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "attempt": {
            "type": "integer",
            "description": "Which try of the delivery this was, from 1."
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "retry_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the next try is due."
          },
          "gave_up": {
            "type": "boolean",
            "description": "Whether this was the last try."
          },
          "manual": {
            "type": "boolean",
            "description": "Whether somebody asked for this try: a test or a redelivery."
          }
        },
        "required": [
          "id",
          "delivery_id",
          "event",
          "seq",
          "ok",
          "status",
          "error",
          "attempt",
          "at",
          "retry_at",
          "gave_up",
          "manual"
        ],
        "description": "One try at sending an event to a webhook."
      },
      "ConversationField": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Its id, in `fields`, in `field.<key>=` and in search. It never changes."
          },
          "label": {
            "type": "string",
            "description": "Its name, as people read it."
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "number",
              "date",
              "boolean",
              "url",
              "choice"
            ]
          },
          "options": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Only for a `choice`."
          },
          "position": {
            "type": "integer",
            "description": "Where it is shown, from 1."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "key",
          "label",
          "type",
          "position",
          "created_at",
          "updated_at"
        ],
        "description": "A field the team keeps on every conversation."
      },
      "SavedReply": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "html_body": {
            "type": "string"
          },
          "text_body": {
            "type": "string"
          },
          "inline_images": {
            "type": "object",
            "additionalProperties": true,
            "description": "Signed links to draw the body's pictures by, keyed by the `/uploads/{hash}` the body names them by."
          },
          "attachments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Attachment"
            },
            "description": "Its files. `POST /api/drafts/{id}/saved_reply_files` copies them onto a draft."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "subject",
          "html_body",
          "text_body",
          "inline_images",
          "attachments",
          "created_at",
          "updated_at"
        ],
        "description": "A saved reply: words to start a reply with."
      },
      "Statistics": {
        "type": "object",
        "properties": {
          "period_days": {
            "type": "integer"
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "summary": {
            "type": "object",
            "properties": {
              "conversations_started": {
                "type": "integer"
              },
              "conversations_open": {
                "type": "integer"
              },
              "awaiting_reply": {
                "type": "integer"
              },
              "first_response_median_sec": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "`null` when nothing was answered."
              },
              "first_responses_counted": {
                "type": "integer"
              }
            },
            "required": [
              "conversations_started",
              "conversations_open",
              "awaiting_reply",
              "first_response_median_sec",
              "first_responses_counted"
            ]
          },
          "daily_volume": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string",
                  "description": "`YYYY-MM-DD`."
                },
                "inbound": {
                  "type": "integer"
                },
                "outbound": {
                  "type": "integer"
                }
              },
              "required": [
                "date",
                "inbound",
                "outbound"
              ]
            }
          },
          "needs_attention": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "subject": {
                  "type": "string"
                },
                "waiting_seconds": {
                  "type": "integer"
                },
                "waiting_over_24h": {
                  "type": "boolean"
                },
                "waiting_over_8h": {
                  "type": "boolean"
                },
                "url": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "subject",
                "waiting_seconds",
                "waiting_over_24h",
                "waiting_over_8h",
                "url"
              ]
            },
            "description": "Conversations waiting longest for a reply."
          },
          "members": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "member": {
                  "$ref": "#/components/schemas/Member"
                },
                "replies_sent": {
                  "type": "integer"
                },
                "notes_added": {
                  "type": "integer"
                },
                "conversations_archived": {
                  "type": "integer"
                },
                "away": {
                  "type": [
                    "object",
                    "null"
                  ],
                  "additionalProperties": true,
                  "description": "When the member is away, until when."
                }
              },
              "required": [
                "member",
                "replies_sent",
                "notes_added",
                "conversations_archived",
                "away"
              ]
            }
          },
          "response_time_trend": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "median_sec": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "responses": {
                  "type": "integer"
                }
              },
              "required": [
                "date",
                "median_sec",
                "responses"
              ]
            }
          },
          "awaiting_reply_trend": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "date": {
                  "type": "string"
                },
                "awaiting_reply": {
                  "type": "integer"
                }
              },
              "required": [
                "date",
                "awaiting_reply"
              ]
            }
          },
          "conversation_depth": {
            "type": "object",
            "properties": {
              "conversations": {
                "type": "integer"
              },
              "average_messages": {
                "type": "number"
              },
              "buckets": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "min_messages": {
                      "type": "integer"
                    },
                    "max_messages": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "conversations": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "label",
                    "min_messages",
                    "max_messages",
                    "conversations"
                  ]
                }
              }
            },
            "required": [
              "conversations",
              "average_messages",
              "buckets"
            ]
          }
        },
        "required": [
          "period_days",
          "from",
          "to",
          "summary",
          "daily_volume",
          "needs_attention",
          "members",
          "response_time_trend",
          "awaiting_reply_trend",
          "conversation_depth"
        ],
        "description": "The team's figures over a period."
      },
      "Installation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "client_id": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the app may ask for."
          },
          "granted_scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What this token's grant holds."
          },
          "manifest_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "manifest_hash": {
            "type": "string"
          },
          "webhook": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Webhook"
              },
              {
                "type": "null"
              }
            ]
          },
          "card_source": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "url": {
                "type": "string"
              },
              "active": {
                "type": "boolean"
              }
            },
            "required": [
              "id",
              "url",
              "active"
            ]
          },
          "mailboxes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Mailbox"
            },
            "description": "The mailboxes the app can see."
          },
          "installed_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "client_id",
          "scopes",
          "granted_scopes",
          "manifest_url",
          "manifest_hash",
          "webhook",
          "card_source",
          "mailboxes",
          "installed_at",
          "updated_at"
        ],
        "description": "An app the team installed from a manifest."
      },
      "Autoresponder": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "message": {
            "type": "string",
            "description": "HTML."
          },
          "first_message_only": {
            "type": "boolean"
          },
          "mark_conversation_as_read": {
            "type": "boolean"
          }
        },
        "required": [
          "enabled",
          "message",
          "first_message_only",
          "mark_conversation_as_read"
        ],
        "description": "The team's default autoresponder."
      }
    },
    "examples": {
      "conversation": {
        "summary": "A conversation.",
        "value": {
          "conversation": {
            "id": "broken-widget-k3f9zq",
            "subject": "Broken widget",
            "thread_subject": "Broken widget",
            "status": "open",
            "snoozed_until": null,
            "message_count": 3,
            "created_at": "2026-03-04T08:00:00Z",
            "last_activity_at": "2026-03-04T09:20:00Z",
            "last_message_at": "2026-03-04T09:12:00Z",
            "has_draft": false,
            "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq",
            "messages_url": "https://jelly.acme.test/api/conversations/broken-widget-k3f9zq/messages",
            "notes_url": "https://jelly.acme.test/api/conversations/broken-widget-k3f9zq/notes",
            "markdown_url": "https://jelly.acme.test/api/conversations/broken-widget-k3f9zq/markdown",
            "labels": [
              {
                "id": "billing",
                "name": "Billing",
                "colour": "lemon",
                "created_at": "2026-01-02T10:00:00Z"
              }
            ],
            "assignees": [
              {
                "id": 3,
                "name": "Sarah Chen",
                "email": "sarah@acme.test",
                "role": "admin",
                "active": true
              }
            ],
            "mailboxes": [
              {
                "id": 1,
                "name": "Support",
                "default": true
              }
            ],
            "contact": {
              "id": "jane-customer",
              "email": "jane@customer.test",
              "name": "Jane Customer"
            },
            "snippet": "Thanks for getting back to us so quickly…",
            "latest_note": null,
            "last_from": {
              "name": "Jane Customer",
              "email": "jane@customer.test"
            },
            "last_from_me": false,
            "has_attachments": true,
            "stamp": "lz3k9f:0:-"
          }
        }
      },
      "draft": {
        "summary": "A draft, as returned to a personal token.",
        "value": {
          "draft": {
            "id": 41,
            "conversation_id": "broken-widget-k3f9zq",
            "subject": "Re: Broken widget",
            "html_body": "<p>Thanks Jane, a new one is on its way.</p>",
            "inline_images": {},
            "to": [
              "Jane Customer <jane@customer.test>"
            ],
            "cc": [],
            "bcc": [],
            "from": {
              "id": 1,
              "address": "support@acme.test",
              "name": "Support",
              "from_name": "Sarah Chen (Support)"
            },
            "mailbox_id": null,
            "locked": true,
            "editor": {
              "type": "member",
              "id": 3,
              "name": "Sarah Chen"
            },
            "started_by": {
              "type": "member",
              "id": 3,
              "name": "Sarah Chen"
            },
            "last_edited_by": {
              "type": "member",
              "id": 3,
              "name": "Sarah Chen"
            },
            "attachments": [],
            "scheduled_for": null,
            "scheduled_by": null,
            "created_at": "2026-03-04T09:10:00Z",
            "updated_at": "2026-03-04T09:12:00Z",
            "url": "https://jelly.acme.test/conversations/broken-widget-k3f9zq",
            "lock_token": "2BxI…"
          },
          "quoted": ""
        }
      }
    }
  }
}
