{
  "openapi": "3.0.3",
  "info": {
    "title": "Oie Engine API",
    "version": "0.30.0",
    "description": "Native engine API for account metadata, XMPP connections, text conversations, reply composition, explicit room sessions, durable message acceptance, and replayable SSE. Credentials are write-only and stored only by the owning engine. All timestamps are UTC and entity/operation IDs are UUIDv7. HTML belongs to the BFF. Known problem responses link to their catalog entry in the Link header with rel=\"help\". Send Prefer: oie-problem-documentation to include the URL in the Problem Details body.",
    "contact": {
      "name": "Arai Hû",
      "url": "https://github.com/araihu/oie"
    }
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8080",
      "description": "Local development engine"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "System",
      "description": "Engine identity and readiness"
    },
    {
      "name": "Accounts",
      "description": "Protocol account metadata"
    },
    {
      "name": "Operations",
      "description": "Durable mutation receipts"
    },
    {
      "name": "Events",
      "description": "Recoverable engine changes"
    },
    {
      "name": "Conversations",
      "description": "Account-scoped direct conversations and recovered read-only rooms"
    },
    {
      "name": "Messages",
      "description": "Retained text messages and durable send intents"
    },
    {
      "name": "Previews",
      "description": "Cached structured link cards and explicitly downloaded images"
    },
    {
      "name": "SharedContent",
      "description": "Source-linked media, documents and links across retained conversations"
    }
  ],
  "paths": {
    "/api/v1/contacts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "listContacts",
        "summary": "Search saved protocol contacts",
        "description": "Read the retained per-account contact directory without fetching a roster, changing subscriptions, adding contacts or opening conversations. Unknown and unavailable sources are distinct from an observed empty roster. Cached contacts remain searchable while disconnected. Results are ordered by stable contact UUID and filtered before pagination.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Optional trimmed UTF-8 text, at most 200 Unicode code points. Matches saved name, address, groups and account label.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Restrict contacts to one owning account.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "$ref": "#/components/parameters/SearchAccountIDs"
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque position bound to the engine, account and trimmed query.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of contacts returned.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Saved contacts with independent provider observation coverage",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/search": {
      "get": {
        "tags": [
          "Messages"
        ],
        "operationId": "searchMessages",
        "summary": "Search retained message text and available metadata",
        "description": "Search the complete retained engine history before pagination, without network retrieval, enrichment or read-state changes. Matching follows shared-content accent/case folding, word prefixes and bounded typing-error tolerance. Current canonical message content is searched by default. Include edit history adds separately labelled verified earlier content revisions, never transport-state revisions or independent correction aliases. Current associated URLs, filenames and cached page titles/descriptions are searched only while their catalog source is current. Metadata coverage can lag immediate message-text coverage. Unreleased schedules and scheduled sends cancelled before dispatch are excluded. Order is newest message position (`timelineAt`, falling back to `createdAt`) at UTC millisecond precision and UUID first, current hit before earlier content revisions, then newest revision timestamp and UUID. New arrivals or corrections can change subsequent results; pages are not a frozen snapshot.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Trimmed nonblank UTF-8 search text, at most 200 Unicode code points; NUL is invalid.",
            "example": "software livre",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "description": "Restrict to one owning account, or omit for all accounts.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "$ref": "#/components/parameters/SearchAccountIDs"
          },
          {
            "name": "conversationId",
            "in": "query",
            "description": "Restrict to one conversation. Its owning account must be included in any supplied account filter.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "includeEditHistory",
            "in": "query",
            "description": "Include retained verified earlier content revisions with explicit historical labels.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque keyset cursor bound to this engine, trimmed query, account, conversation and history filter. Reuse with changed scope is invalid.",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum returned hits. Filtering is applied before this limit.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bounded message hits and current metadata coverage, including an empty page when nothing matches",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageSearchPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/shared-content": {
      "get": {
        "tags": [
          "SharedContent"
        ],
        "operationId": "listSharedContent",
        "summary": "Browse shared content across retained conversations",
        "description": "Browse the local derived catalog, newest message position (`timelineAt`, falling back to `createdAt`) first with item UUID ties at millisecond precision. Unreleased schedules and scheduled sends cancelled before dispatch are excluded. Account/conversation filters apply before pagination. Metadata search covers URL, filename, visible caption, cached preview title/description, sender, conversation and account label. A background worker indexes visible retained and incoming messages. Listing registers preview enrichment only for the returned image/link references; originals still follow device preferences and explicit download operations. Document/audio entries remain URL references. Video bytes are available only through the explicit content operation; listing does not fetch video metadata or bytes. Backfill or new arrivals may insert items before the current cursor; reload to include them. A conversation filter outside the selected accounts is invalid.",
        "parameters": [
          {
            "name": "type",
            "description": "Content category; media combines image, audio and video references; video restricts to video before pagination.",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "media",
                "links",
                "documents",
                "video"
              ],
              "default": "media"
            }
          },
          {
            "name": "accountId",
            "description": "Restrict to one owning account. Omit both account filters to list all accounts.",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "$ref": "#/components/parameters/SearchAccountIDs"
          },
          {
            "name": "conversationId",
            "description": "Restrict to one source conversation, or omit for all conversations.",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "messageId",
            "description": "Restrict to at most one hundred unique source message UUIDs. Requires conversationId; filters before pagination.",
            "in": "query",
            "style": "form",
            "explode": true,
            "example": [
              "019a5df0-0000-7000-8000-000000000101"
            ],
            "schema": {
              "type": "array",
              "maxItems": 100,
              "minItems": 1,
              "uniqueItems": true,
              "items": {
                "$ref": "#/components/schemas/ID"
              }
            }
          },
          {
            "name": "q",
            "description": "Accent- and case-insensitive metadata search, applied before pagination. Exact substrings and word prefixes match. Otherwise all query terms must match a metadata word or prefix, in any order. Terms with letters of four to seven characters allow one insertion, deletion, substitution or adjacent transposition; terms of eight or more allow two. Shorter terms, pure numbers and symbols require exact matches. Result order remains chronological.",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque source-millisecond/item-UUID keyset cursor returned by this operation.",
            "schema": {
              "type": "string",
              "maxLength": 512
            }
          },
          {
            "name": "limit",
            "description": "Maximum returned items, independent of indexing batch size.",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Shared items and catalog indexing progress in the selected account/conversation scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SharedContentPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/shared-content/{itemId}/thumbnail": {
      "parameters": [
        {
          "name": "itemId",
          "in": "path",
          "required": true,
          "description": "Retained video reference UUID. Arbitrary provider addresses are not accepted.",
          "schema": {
            "$ref": "#/components/schemas/ID"
          }
        }
      ],
      "get": {
        "tags": [
          "Previews"
        ],
        "operationId": "getVideoThumbnail",
        "summary": "Read a retained video thumbnail state",
        "description": "Reads local cache state for a current video reference without retrieving provider bytes. A missing cache record is represented as not_requested. Removed references return 404.",
        "responses": {
          "200": {
            "description": "Current source-linked thumbnail metadata, including not requested or unavailable state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoThumbnail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Previews"
        ],
        "operationId": "requestVideoThumbnail",
        "summary": "Ensure a cached video thumbnail",
        "description": "Queues bounded frame extraction for a current retained video reference. Concurrent requests share work; a ready thumbnail is reused. A failed extraction can be explicitly retried. A retained HTTP(S) thumbnail supplied in XMPP file metadata is preferred (at most 1 MiB). Otherwise background extraction reads at most 4 MiB from the beginning and 1 MiB from the end, with a 15 second job deadline, and never retains an original video. Small source videos may fit entirely within that budget. The source is revalidated before publication. Does not send messages or start playback. Missing both a supplied thumbnail and decoder capability returns 503 without provider retrieval.",
        "responses": {
          "200": {
            "description": "Thumbnail already retained",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoThumbnail"
                }
              }
            }
          },
          "202": {
            "description": "Thumbnail extraction queued or in progress",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VideoThumbnail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/shared-content/{itemId}/content": {
      "get": {
        "tags": [
          "SharedContent"
        ],
        "operationId": "getSharedContentMedia",
        "summary": "Stream an explicitly selected video or supported document reference",
        "description": "Reads the current retained canonical source before opening its URL through the engine's public-network policy. Never redirects the browser to the provider. Supports one byte range and If-Range; an upstream that ignores Range returns 200 rather than an invented 206. Video accepts MP4, WebM, Ogg video and QuickTime MIME types up to 2 GiB; codec playback remains a browser capability. Document references are restricted by URL extension and provider MIME: .txt/.log/.md/.csv/.json/.xml are served as text/plain up to 2 MiB and .pdf as application/pdf up to 100 MiB. Text MIME must be text/plain or the matching format's text/markdown, text/csv, application/json, text/json, application/xml or text/xml. HTML, SVG, office documents and mismatched MIME types are unsupported. A known complete size is required. This is explicit on-demand streaming, not a retained download: no full-file hash, sender verification, offline availability or transfer state is implied. Listing/mounting UI must not call this operation. Client disconnect cancels the upstream read. HEAD returns 405 without contacting the provider. Multiple or malformed ranges are invalid. JSON, XML and Markdown are returned as plain text, never active markup.",
        "parameters": [
          {
            "name": "itemId",
            "in": "path",
            "required": true,
            "description": "Current supported catalog item UUID, never an arbitrary provider URL.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "Range",
            "in": "header",
            "description": "One bytes range, including an open-ended or suffix range.",
            "schema": {
              "type": "string",
              "maxLength": 256
            }
          },
          {
            "name": "If-Range",
            "in": "header",
            "description": "A strong entity tag or HTTP date previously returned for this resource. Ignored without Range.",
            "schema": {
              "type": "string",
              "maxLength": 1024
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Complete media/document response; the provider may have ignored Range.",
            "headers": {
              "Accept-Ranges": {
                "description": "Byte-range capability for this source.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "bytes",
                    "none"
                  ]
                }
              },
              "Content-Disposition": {
                "description": "Inline content with a sanitized retained source filename.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Exact response byte count.",
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                }
              },
              "Content-Range": {
                "description": "Validated bytes start-end/total for a partial response; absent for 200.",
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "description": "Sanitized provider entity validator, not a computed or verified digest.",
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "Validated provider HTTP date when supplied.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/webm": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/ogg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/quicktime": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "Validated partial media/document response.",
            "headers": {
              "Accept-Ranges": {
                "description": "Byte-range capability for this source.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "bytes",
                    "none"
                  ]
                }
              },
              "Content-Disposition": {
                "description": "Inline content with a sanitized retained source filename.",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Length": {
                "description": "Exact response byte count.",
                "schema": {
                  "type": "integer",
                  "format": "int64",
                  "minimum": 0
                }
              },
              "Content-Range": {
                "description": "Validated bytes start-end/total for a partial response; absent for 200.",
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "description": "Sanitized provider entity validator, not a computed or verified digest.",
                "schema": {
                  "type": "string"
                }
              },
              "Last-Modified": {
                "description": "Validated provider HTTP date when supplied.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/webm": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/ogg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "video/quicktime": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Malformed or multiple byte ranges, invalid validators, or invalid identity.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "The current retained supported reference does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Only GET is supported; no provider request occurs.",
            "headers": {
              "Allow": {
                "description": "GET is the only supported method.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "GET"
                  ]
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "413": {
            "description": "The complete source exceeds its type-specific streaming limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "The provider MIME type does not match the retained supported reference.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "416": {
            "description": "Requested range cannot be satisfied.",
            "headers": {
              "Content-Range": {
                "description": "bytes */total when the complete source size is known.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "The provider destination is blocked by the public-network policy.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "502": {
            "description": "Provider response or range metadata is invalid, unavailable, or truncated.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Streaming is unavailable or the bounded concurrent-reader limit is reached.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "504": {
            "description": "The provider read timed out.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/messages/{messageId}/context": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        },
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "get": {
        "operationId": "getMessageContext",
        "tags": [
          "Messages"
        ],
        "summary": "Read a bounded timeline around a retained message",
        "description": "Reads the target and nearest visible retained neighbors from the same conversation, newest first by timelineAt or createdAt at UTC millisecond precision and UUID tie-break. Includes the target even with limit 1. Only local retained data is read; no provider archive queries or read receipts are triggered. Foreign or unavailable source messages return not_found, including unreleased schedules and scheduled sends cancelled before dispatch. Clients should offer return to latest when hasNewer is true. A retained correction-source alias resolves to the original logical targetId with requestedTargetId identifying the requested alias. All returned items remain scoped to this conversation.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum window size, including the target.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Retained context window including the target",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageContext"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/star": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "get": {
        "operationId": "getMessageStar",
        "tags": [
          "Messages"
        ],
        "summary": "Read the source message star",
        "description": "App-owned state independent of message content revisions and protocol support. Before the first change, starred is false and revisionId and updatedAt are null. Reading does not create state or provider traffic.",
        "responses": {
          "200": {
            "description": "Current star state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageStar"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "setMessageStar",
        "tags": [
          "Messages"
        ],
        "summary": "Star or unstar a retained source message",
        "description": "Atomically compares the star revision, stores an independent immutable star revision, a durable operation receipt and an event. Does not edit message content, bump chat activity, change read state or send anything to the provider. The precondition is null for unchanged initial state. A stale precondition returns state_conflict with currentStar. Replays return the original receipt snapshot, even if later changes occurred; read current state after an uncertain response. An already matching value records a receipt without a new event.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable UUID for this exact star operation, retained across retries.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Desired state and the previously observed star revision.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetMessageStar"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Committed star state or original operation snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageStar"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/read-state": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "getConversationReadState",
        "tags": [
          "Conversations"
        ],
        "summary": "Read retained conversation read state",
        "description": "Reads app-owned read state without changing messages or sending provider receipts.",
        "responses": {
          "200": {
            "description": "Current read state and unread count",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationReadState"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "markConversationMessagesRead",
        "tags": [
          "Conversations"
        ],
        "summary": "Mark specifically observed incoming messages read",
        "description": "Atomically merges the listed retained message identities into app-owned read state. Other messages, including later arrivals, remain unread. Lists are bounded and must belong to this conversation. Own and historical messages are ignored. Repeated batches are safe; retries with the same key return the original committed snapshot. Read revisions and operation receipts are durable. Reading does not bump conversation activity, edit content or send protocol receipts. This alpha operation records local engine read evidence; receipt emission and independent-engine synchronization remain deferred.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable UUID for this exact read batch, reused after an uncertain response.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Message identities observed in the active focused conversation.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MarkMessagesRead"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Committed read state or original operation snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationReadState"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/message-stars": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "listConversationMessageStars",
        "tags": [
          "Messages"
        ],
        "summary": "Read star states for displayed messages",
        "description": "Reads up to 100 distinct source message states without creating rows. Every message must belong to the requested conversation; foreign or missing message IDs reject the whole request. Item order is unspecified.",
        "parameters": [
          {
            "name": "messageId",
            "in": "query",
            "required": true,
            "example": [
              "019a5df0-0000-7000-8000-000000000001"
            ],
            "description": "Source message UUIDs to read, one query parameter per UUID.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "example": [
                "019a5df0-0000-7000-8000-000000000001"
              ],
              "minItems": 1,
              "maxItems": 100,
              "items": {
                "$ref": "#/components/schemas/ID"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current states, including explicit false and null revisions for untouched messages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageStarPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/starred-messages": {
      "get": {
        "operationId": "listStarredMessages",
        "tags": [
          "Messages"
        ],
        "summary": "Browse retained starred messages with their source context",
        "description": "Lists current starred messages across accounts or within an account/conversation. Order is source message UTC timestamp at millisecond precision descending, then message UUID descending. Unstarring a cursor message does not break paging. This is a live collection, not a frozen snapshot; newer stars can appear before the current page. No provider requests, enrichment or downloads are started. Conversation favorites are independent of message stars.",
        "parameters": [
          {
            "name": "accountId",
            "in": "query",
            "description": "Optional owning account filter.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "conversationId",
            "in": "query",
            "description": "Optional source conversation filter; it must agree with accountId when both are supplied.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Message UUID from nextCursor; its retained source message must belong to the selected scope.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of starred messages per page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current starred-message page",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StarredMessagePage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/previews": {
      "get": {
        "tags": [
          "Previews"
        ],
        "operationId": "listConversationPreviews",
        "summary": "Read cached message link and image previews",
        "description": "Returns at most three URL previews per retained message. Without messageId, uses the latest fifty messages. Cache misses queue bounded background page metadata enrichment. Original images are fetched only by the explicit download operation. Message text and revisions are unchanged.",
        "parameters": [
          {
            "name": "conversationId",
            "in": "path",
            "required": true,
            "description": "Stable entity UUID.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "messageId",
            "in": "query",
            "required": false,
            "description": "Message UUIDs from this conversation, up to one hundred. Omit for the latest fifty messages.",
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "maxItems": 100,
              "minItems": 1,
              "uniqueItems": true,
              "example": [
                "019a5df0-0000-7000-8000-000000000001"
              ],
              "items": {
                "$ref": "#/components/schemas/ID"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cached structured previews, including queued or unavailable metadata",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PreviewPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/link-previews": {
      "post": {
        "tags": [
          "Previews"
        ],
        "operationId": "resolveDraftLinkPreview",
        "summary": "Resolve a link preview for a draft",
        "description": "Fetches metadata and an optional bounded raster image for one public HTTP(S) URL. Uses the transient link cache and creates no message, durable preview record or original download.",
        "requestBody": {
          "required": true,
          "description": "Only the URL selected for preview.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DraftLinkPreviewRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Metadata and optional raster image for the submitted URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DraftLinkPreview"
                }
              }
            }
          },
          "204": {
            "description": "Metadata is unavailable. The client may still display the URL as a link."
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/previews/{previewId}": {
      "get": {
        "tags": [
          "Previews"
        ],
        "operationId": "getMessagePreview",
        "summary": "Read a message preview",
        "description": "Reads cached metadata and original image download state. Does not initiate a download.",
        "parameters": [
          {
            "name": "previewId",
            "in": "path",
            "required": true,
            "description": "Stable entity UUID.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current preview state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagePreview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/previews/{previewId}/refresh": {
      "post": {
        "tags": [
          "Previews"
        ],
        "operationId": "refreshMessagePreview",
        "summary": "Refresh cached link metadata",
        "description": "Explicitly queues fresh metadata for a current retained link reference, bypassing the metadata cache. Existing metadata remains usable during refresh or failure. Concurrent pending refreshes share queued work. Does not edit a message or request an original image download.",
        "parameters": [
          {
            "name": "previewId",
            "in": "path",
            "required": true,
            "description": "Stable preview entity UUID.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Refresh queued with existing metadata preserved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagePreview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/previews/{previewId}/download": {
      "post": {
        "tags": [
          "Previews"
        ],
        "operationId": "downloadPreviewImage",
        "summary": "Retain the original image for a preview",
        "description": "Explicitly requests the original for a kind=image reference. This ensure operation is safe to retry: concurrent requests share queued work and an already retained file is reused. Failed downloads can be retried. Only validated PNG, JPEG and GIF images up to 32 MiB and bounded pixel dimensions are retained. The local SHA-256 fingerprint is computed-only, not evidence of sender-provided integrity. Link cards do not support this operation.",
        "parameters": [
          {
            "name": "previewId",
            "in": "path",
            "required": true,
            "description": "Stable entity UUID.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Original already retained",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagePreview"
                }
              }
            }
          },
          "202": {
            "description": "Download queued or in progress",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagePreview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/media/{hash}": {
      "get": {
        "tags": [
          "Previews"
        ],
        "operationId": "getMediaImage",
        "summary": "Read a retained preview or original image",
        "description": "Serves only local hash-verified PNG, JPEG or GIF bytes, without fetching the provider. Thumbnails are normalized static PNGs or JPEGs, up to 1024 pixels per axis; originals retain their validated media type. No remote redirects.",
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "description": "Lowercase SHA-256 digest of the retained file.",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{64}$"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "required": false,
            "description": "Previously returned image ETag.",
            "schema": {
              "type": "string",
              "maxLength": 1024
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Retained image bytes. Content-Type identifies PNG, JPEG or GIF.",
            "headers": {
              "ETag": {
                "description": "Quoted content digest.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "Private immutable cache policy.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/gif": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "Matching ETag; no response body."
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/health": {
      "get": {
        "tags": [
          "System"
        ],
        "operationId": "getHealth",
        "summary": "Check engine readiness",
        "description": "Returns ready only when the database can be queried.",
        "responses": {
          "200": {
            "description": "Engine ready",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                },
                "example": {
                  "status": "ready"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/engine": {
      "get": {
        "tags": [
          "System"
        ],
        "operationId": "getEngine",
        "summary": "Read the persistent engine identity",
        "description": "Read eventCursor before fetching snapshots to replay changes during the snapshot.",
        "responses": {
          "200": {
            "description": "Engine identity and current event boundary",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Engine"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000003",
                  "role": "local",
                  "apiVersion": "0.30.0",
                  "eventCursor": "019a5df0-0000-7000-8000-000000000003:1"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/engine/reset": {
      "post": {
        "tags": [
          "System"
        ],
        "operationId": "resetEngineData",
        "summary": "Delete all data held by this engine",
        "description": "Permanently deletes accounts, credentials, history and files held by the engine and starts an empty engine with a new identity. Provider copies are outside this operation.",
        "requestBody": {
          "description": "The active engine identity and the literal confirmation word.",
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "engineId": "019a5df0-0000-7000-8000-000000000003",
                "confirmation": "delete"
              },
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "engineId",
                  "confirmation"
                ],
                "properties": {
                  "engineId": {
                    "$ref": "#/components/schemas/ID"
                  },
                  "confirmation": {
                    "type": "string",
                    "example": "delete",
                    "enum": [
                      "delete"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Data deleted and empty engine ready."
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts": {
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "listAccounts",
        "summary": "List registered account metadata",
        "description": "Stable UUID-ordered keyset pagination; registration does not establish an XMPP session.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "description": "Continue after this account UUID.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of accounts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountPage"
                },
                "example": {
                  "items": [
                    {
                      "id": "019a5df0-0000-7000-8000-000000000001",
                      "revisionId": "019a5df0-0000-7000-8000-000000000002",
                      "protocol": "xmpp",
                      "address": "alice@localhost",
                      "label": "Personal",
                      "connectionState": "not_connected",
                      "createdAt": "2026-11-07T12:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Accounts"
        ],
        "operationId": "registerAccount",
        "summary": "Register protocol account metadata",
        "description": "Atomically stores an account, initial immutable revision, operation receipt,\nand account.registered event. Repeating a key with the same normalized input\nreturns the original result. Reusing it with different input conflicts.\nNo credentials are accepted, and no protocol session is opened.\nNew XMPP registrations use the compiled-in adapter to normalize bare JIDs before matching. Existing foundation metadata is retained without rewriting identities.\n",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Caller-generated UUIDv7, retained for the lifetime of the engine data.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "description": "Metadata for an existing protocol identity.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterAccount"
              },
              "example": {
                "protocol": "xmpp",
                "address": "alice@localhost",
                "label": "Personal"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Original account result replayed without another side effect",
            "headers": {
              "Idempotency-Replayed": {
                "description": "True for a repeated successful operation.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000002",
                  "protocol": "xmpp",
                  "address": "alice@localhost",
                  "label": "Personal",
                  "connectionState": "not_connected",
                  "createdAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "201": {
            "description": "Account registered",
            "headers": {
              "Location": {
                "description": "Account API resource.",
                "schema": {
                  "type": "string"
                }
              },
              "Idempotency-Replayed": {
                "description": "False for the first successful execution.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000002",
                  "protocol": "xmpp",
                  "address": "alice@localhost",
                  "label": "Personal",
                  "connectionState": "not_connected",
                  "createdAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountID"
        }
      ],
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "getAccount",
        "summary": "Read an account",
        "description": "Returns registered metadata and its initial current revision.",
        "responses": {
          "200": {
            "description": "Registered account",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000002",
                  "protocol": "xmpp",
                  "address": "alice@localhost",
                  "label": "Personal",
                  "connectionState": "not_connected",
                  "createdAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}/avatars": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountID"
        }
      ],
      "get": {
        "operationId": "listAccountAvatars",
        "tags": [
          "Accounts"
        ],
        "summary": "Read retained profile image metadata",
        "description": "Account-scoped user and conversation profile images retrieved through the protocol. Initials remain the fallback for absent or inaccessible images. Image bytes live outside SQLite and contain only normalized PNG thumbnails.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "description": "Continue after this avatar record UUID.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Known profile metadata, ordered by stable avatar UUID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AvatarPage"
                },
                "example": {
                  "items": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/avatars/{hash}": {
      "parameters": [
        {
          "name": "hash",
          "in": "path",
          "required": true,
          "description": "SHA-256 of the normalized PNG thumbnail.",
          "schema": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          }
        }
      ],
      "get": {
        "operationId": "getAvatarImage",
        "tags": [
          "Accounts"
        ],
        "summary": "Read a cached profile thumbnail",
        "description": "Returns only a locally retained, decoded and normalized image; never redirects to provider URLs. Hash-addressed images can be cached privately and immutably.",
        "parameters": [
          {
            "name": "If-None-Match",
            "in": "header",
            "description": "Previously received content ETag.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Static PNG thumbnail, at most 256 pixels on either axis",
            "headers": {
              "ETag": {
                "description": "Quoted content hash.",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "Private immutable cache policy.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "304": {
            "description": "The supplied ETag still identifies this retained image"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}/history": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountID"
        }
      ],
      "get": {
        "operationId": "getAccountHistory",
        "tags": [
          "Accounts"
        ],
        "summary": "Read account history recovery progress",
        "description": "Recovery starts on protocol connection. Saved room entries and authorized text archives are read serially, with persistent checkpoints and deduplication. Historical imports do not create sends or unread counts. Complete means the last authorized text recovery pass finished; it is not a full protocol-feature claim. Saved rooms are read-only in this increment. Provider permission failures remain partial coverage.",
        "responses": {
          "200": {
            "description": "Persistent recovery state and retained counts",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HistoryStatus"
                },
                "example": {
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "state": "not_started",
                  "retainedMessages": 0,
                  "conversations": 0,
                  "updatedAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}/capabilities": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountID"
        }
      ],
      "get": {
        "tags": [
          "Accounts"
        ],
        "operationId": "getAccountCapabilities",
        "summary": "Read account capability evidence",
        "description": "Read application implementation, protocol support and cached server discovery evidence separately. This query never contacts the protocol server. Evidence is scoped, bounded and cleared when a connection ends. An absent advertisement does not prove that a client-level feature is unsupported. Discovery runs once per connection; room evidence is also collected during history recovery.",
        "responses": {
          "200": {
            "description": "Current account capability evidence",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capabilities"
                },
                "example": {
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "state": "unknown",
                  "reason": "not_connected",
                  "features": [],
                  "discovery": {
                    "state": "not_connected",
                    "services": []
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/operations/{operationId}": {
      "get": {
        "tags": [
          "Operations"
        ],
        "operationId": "getOperation",
        "summary": "Reconcile a mutation after an ambiguous response",
        "description": "A receipt proves the local mutation committed. Send receipts prove durable engine acceptance. Request includeAcceptedMessage to read the immutable send acceptance snapshot, including the original composition and scheduled instant. This route exposes the receipt kinds defined by Operation. An ID without an exposed receipt returns 404. Recover the result of updatePendingMessage or sendScheduledMessageNow by replaying the original request with the same key and input.",
        "parameters": [
          {
            "name": "operationId",
            "in": "path",
            "required": true,
            "description": "The original Idempotency-Key.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "includeAcceptedMessage",
            "in": "query",
            "description": "Include the original acceptance snapshot for a send_message receipt. Other receipt kinds never include a message.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Durable operation receipt",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Operation"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000004",
                  "kind": "register_account",
                  "status": "succeeded",
                  "resourceId": "019a5df0-0000-7000-8000-000000000001",
                  "createdAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "operationId": "streamEvents",
        "x-sse-event-schema": {
          "$ref": "#/components/schemas/Event"
        },
        "summary": "Replay durable changes and follow new events",
        "description": "SSE id is engineUUID:sequence; event is the event type; data is Event JSON.\nLast-Event-ID resumes strictly after its sequence. Without a cursor replay\nfrom zero. Cursors from another engine or ahead of current state return 409\nbefore streaming. Heartbeat comments are not durable events.\nThis engine retains the complete change log. Clients deduplicate by event\nID and refresh snapshots if a future retention policy expires a cursor.\n",
        "parameters": [
          {
            "name": "Last-Event-ID",
            "in": "header",
            "description": "Previously applied engine-scoped cursor.",
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream whose data fields follow the Event schema",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                },
                "example": ": heartbeat\n\n"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}/connection": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountID"
        }
      ],
      "get": {
        "operationId": "getAccountConnection",
        "tags": [
          "Accounts"
        ],
        "summary": "Read account connection state",
        "description": "State is runtime evidence, distinct from saved account metadata.",
        "responses": {
          "200": {
            "description": "Current resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                },
                "example": {
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "state": "not_connected",
                  "updatedAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "configureAccountConnection",
        "tags": [
          "Accounts"
        ],
        "summary": "Configure and connect an account",
        "description": "Persists write-only credentials before starting an asynchronous TLS/SASL connection. Transient network failures reconnect with bounded backoff; invalid credentials or TLS verification failures require corrected configuration. Ordinary engine restart reloads enabled configurations.",
        "responses": {
          "202": {
            "headers": {
              "Location": {
                "description": "Connection state; acceptance is not connection success.",
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                }
              }
            },
            "description": "Connection configuration accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                },
                "example": {
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "state": "not_connected",
                  "updatedAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "requestBody": {
          "required": true,
          "description": "Structured operation input.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConfigureConnection"
              },
              "example": {
                "password": "alice-local-only"
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "disconnectAccount",
        "tags": [
          "Accounts"
        ],
        "summary": "Disconnect and disable automatic connection",
        "description": "Keeps protocol credentials local for a later explicit configuration; does not delete account history or pending sends. Repeating disconnect is safe.",
        "responses": {
          "200": {
            "description": "Current resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Connection"
                },
                "example": {
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "state": "not_connected",
                  "updatedAt": "2026-11-07T12:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}/room-preview": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountID"
        }
      ],
      "get": {
        "operationId": "previewRoom",
        "tags": [
          "Conversations"
        ],
        "summary": "Read a room preview using the selected account",
        "description": "Reads published room information and an available vCard from the bare room address. Does not save the conversation or enter the room.",
        "parameters": [
          {
            "name": "address",
            "in": "query",
            "required": true,
            "description": "Bare XMPP room address.",
            "schema": {
              "type": "string",
              "minLength": 3,
              "maxLength": 2047
            }
          },
          {
            "name": "discoveredServiceOnly",
            "in": "query",
            "description": "Restrict automatic lookups to MUC services advertised by the account server. Other destinations return unavailable without an XMPP request.",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Available public room information or its lookup state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomPreview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/accounts/{accountId}/rooms": {
      "parameters": [
        {
          "$ref": "#/components/parameters/AccountID"
        }
      ],
      "post": {
        "operationId": "openRoom",
        "tags": [
          "Conversations"
        ],
        "summary": "Open and request entry to an XMPP room",
        "description": "Atomically saves the room and its entry intent. Creation requires explicit selection and is confirmed separately through the room session. Repeating the same key cannot reactivate a later leave. Creation uses the service defaults with persistence enabled and an optional room name.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable operation UUID for this request.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Destination and requested room action.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenRoom"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Saved conversation with an accepted or replayed entry request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations": {
      "get": {
        "operationId": "listConversations",
        "tags": [
          "Conversations"
        ],
        "summary": "List retained conversations",
        "description": "Keyset pages ordered by descending engine activity. Search matches conversation address/title and latest text preview; this is not full message search.",
        "responses": {
          "200": {
            "description": "Current resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationPage"
                },
                "example": {
                  "items": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque nextCursor from the preceding page.",
            "schema": {
              "type": "string",
              "maxLength": 100
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "accountId",
            "in": "query",
            "required": false,
            "description": "Stable accountId reference.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "$ref": "#/components/parameters/SearchAccountIDs"
          },
          {
            "name": "q",
            "in": "query",
            "description": "Conversation address/title/latest-preview substring.",
            "schema": {
              "type": "string",
              "maxLength": 200
            }
          },
          {
            "name": "unreadOnly",
            "in": "query",
            "description": "Only conversations with unread messages.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "favoritesOnly",
            "in": "query",
            "description": "Only favorite conversations.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "archived",
            "in": "query",
            "description": "Archived state; defaults to false.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "description": "Conversation kind.",
            "schema": {
              "type": "string",
              "enum": [
                "direct",
                "room"
              ]
            }
          }
        ]
      },
      "post": {
        "operationId": "openConversation",
        "tags": [
          "Conversations"
        ],
        "summary": "Open a direct conversation",
        "description": "Finds an existing conversation for the canonical account/peer or creates one. Durable operation keys retain original results. Does not add a contact or send presence subscriptions.",
        "responses": {
          "200": {
            "description": "Existing conversation or original operation replay",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "kind": "direct",
                  "address": "bob@localhost",
                  "title": "bob@localhost",
                  "lastMessage": "Hello from Oie!",
                  "updatedAt": "2026-11-07T12:00:00Z",
                  "unreadCount": 0,
                  "archived": false,
                  "favorite": false
                }
              }
            }
          },
          "201": {
            "headers": {
              "Location": {
                "description": "New conversation resource.",
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                }
              }
            },
            "description": "Direct conversation opened",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "kind": "direct",
                  "address": "bob@localhost",
                  "title": "bob@localhost",
                  "lastMessage": "Hello from Oie!",
                  "updatedAt": "2026-11-07T12:00:00Z",
                  "unreadCount": 0,
                  "archived": false,
                  "favorite": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable Idempotency-Key reference.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Structured operation input.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenConversation"
              },
              "example": {
                "accountId": "019a5df0-0000-7000-8000-000000000001",
                "address": "bob@localhost"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/room-session": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "getRoomSession",
        "tags": [
          "Conversations"
        ],
        "summary": "Read this engine's room session and durable join intent",
        "description": "Reads local state without joining. Active denotes a durable intent to rejoin, not observed membership. A joined state requires confirmed self-presence. Missing room intent has no intent identifiers.",
        "responses": {
          "200": {
            "description": "Current own room-session projection",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "joinRoom",
        "tags": [
          "Conversations"
        ],
        "summary": "Explicitly request entry and reconnect recovery for a saved room",
        "description": "Durably records the owning engine's join intent before protocol work. This increment supports rooms without a join password. Repeated operation keys do not repeat an established command; changed payloads or stale intent revisions conflict. Reads and reply preparation never join. Protocol confirmation is observed through getRoomSession.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable operation UUID for this join intent.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Desired nickname and observed join-intent revision.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JoinRoom"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Join intent durably accepted or replayed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/room-session/leave": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "post": {
        "operationId": "leaveRoom",
        "tags": [
          "Conversations"
        ],
        "summary": "Leave a room and stop this engine's automatic rejoin",
        "description": "Durably records an explicit leave before sending unavailable presence. Saved history and queued messages are retained. Queued room work cannot dispatch while the intent is inactive. An old operation replay never reactivates a later leave or revoked intent.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable operation UUID for this leave intent.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Observed join-intent revision.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LeaveRoom"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Leave intent durably accepted or replayed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/mention-candidates": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "tags": [
          "Conversations"
        ],
        "operationId": "getMentionCandidates",
        "summary": "Read observed room mention candidates",
        "description": "Read a bounded, account-scoped snapshot of visible room occupants. This never joins a room or discovers hidden personal identities. Candidates supply exact readable insertion text. Structured references are available only when the protocol adapter has a valid disclosed target; otherwise sending preserves the same readable text without protocol metadata.",
        "responses": {
          "200": {
            "description": "Available room candidates and explicit observation state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MentionCandidates"
                },
                "example": {
                  "conversationId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000002",
                  "state": "known",
                  "items": [
                    {
                      "id": "room@conference.example/Juliet",
                      "displayName": "Juliet",
                      "insertText": "@Juliet",
                      "referenceMode": "text"
                    }
                  ],
                  "truncated": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/archive": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "getRoomArchive",
        "tags": [
          "Conversations"
        ],
        "summary": "Read room archive recovery settings and progress",
        "description": "Reads retained state for the room without sending a protocol request.",
        "responses": {
          "200": {
            "description": "Current room archive state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomArchive"
                },
                "example": {
                  "conversationId": "019a5df0-0000-7000-8000-000000000003",
                  "mode": "gradual",
                  "initialComplete": false,
                  "olderComplete": false,
                  "availability": "supported",
                  "textSearch": "unsupported"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "put": {
        "operationId": "setRoomArchiveMode",
        "tags": [
          "Conversations"
        ],
        "summary": "Set the room archive recovery mode",
        "description": "Replaces the mode idempotently. Existing messages and progress are retained. Gradual mode resumes eligible automatic recovery.",
        "requestBody": {
          "description": "Requested archive action or room recovery preference.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetRoomArchiveMode"
              },
              "example": {
                "mode": "on_demand"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Saved mode and current progress",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomArchive"
                },
                "example": {
                  "conversationId": "019a5df0-0000-7000-8000-000000000003",
                  "mode": "gradual",
                  "initialComplete": false,
                  "olderComplete": false,
                  "availability": "supported",
                  "textSearch": "unsupported"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/archive/requests": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "post": {
        "operationId": "requestRoomArchive",
        "tags": [
          "Conversations"
        ],
        "summary": "Request a bounded room archive read",
        "description": "Request the next initial page (focus), an older page (older), context around a retained message (context), or a text search page (search). Search requires a query and accepts an optional cursor from the same room and query. Context requires messageId. Older accepts a retained messageId as a boundary. The owning account and room determine the archive. Successful imports are retained with historical semantics. Search and context do not advance background recovery checkpoints. Results are bounded. A capability failure does not affect local reads.",
        "requestBody": {
          "description": "Requested archive action or room recovery preference.",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoomArchiveRequest"
              },
              "example": {
                "action": "focus"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Archive read outcome and canonical stored messages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomArchiveResult"
                },
                "example": {
                  "archive": {
                    "conversationId": "019a5df0-0000-7000-8000-000000000003",
                    "mode": "gradual",
                    "initialComplete": false,
                    "olderComplete": false,
                    "availability": "supported",
                    "textSearch": "unsupported"
                  },
                  "outcome": "stored",
                  "items": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/details": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "tags": [
          "Conversations"
        ],
        "operationId": "getConversationDetails",
        "summary": "Read scoped conversation details",
        "description": "Read the current account connection and session-local conversation metadata. Returns immediately; a connected adapter may schedule a deduplicated, serialized discovery refresh no more often than once per sixty seconds. This never joins a room, requests a subscription, probes a peer or sends a message. Occupant discovery is a bounded snapshot, never a complete member roster. Missing presence remains unknown. Disconnect clears observations.",
        "responses": {
          "200": {
            "description": "Available metadata and explicit field availability for this conversation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationDetails"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "description": "Engine state is temporarily unavailable.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "getConversation",
        "tags": [
          "Conversations"
        ],
        "summary": "Read a conversation",
        "description": "Returns the current conversation projection.",
        "responses": {
          "200": {
            "description": "Current resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "kind": "direct",
                  "address": "bob@localhost",
                  "title": "bob@localhost",
                  "lastMessage": "Hello from Oie!",
                  "updatedAt": "2026-11-07T12:00:00Z",
                  "unreadCount": 0,
                  "archived": false,
                  "favorite": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "patch": {
        "operationId": "updateConversationPreferences",
        "tags": [
          "Conversations"
        ],
        "summary": "Change conversation preferences or accept a direct message request",
        "description": "Atomically updates only the supplied app-owned flags, records an immutable conversation revision and operation receipt, and emits conversation.updated only for an actual change. No protocol operation is performed. The owning account must match accountId. expectedRevisionId must match the latest conversation revision, including intervening incoming-message or read updates. A matching Idempotency-Key replays its original accepted snapshot before checking current state. Reusing a key with different fields or preconditions conflicts. Setting an already-current value succeeds without another revision or event. Activity order, message timestamps, unread counts and outbox are unchanged by archive or favorite changes. Accepting a direct message request reveals the conversation and restores it from the archive. Read the conversation after a replay for current state.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Durable key bound to this exact preference change and precondition.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Owning account, current revision and the flags to change.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateConversationPreferences"
              },
              "example": {
                "accountId": "019a5df0-0000-7000-8000-000000000001",
                "expectedRevisionId": "019a5df0-0000-7000-8000-000000000002",
                "archived": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted conversation snapshot, or its original idempotent replay",
            "headers": {
              "Idempotency-Replayed": {
                "description": "True only when this exact operation was already accepted; the body is its original snapshot.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000002",
                  "accountId": "019a5df0-0000-7000-8000-000000000003",
                  "kind": "direct",
                  "address": "bob@localhost",
                  "title": "Bob",
                  "lastMessage": "Retained message",
                  "updatedAt": "2026-11-07T12:00:00Z",
                  "unreadCount": 0,
                  "archived": true,
                  "favorite": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "listMessages",
        "tags": [
          "Messages"
        ],
        "summary": "Read retained messages",
        "description": "Returns a bounded page of retained messages newest-first by timelineAt when present and createdAt otherwise, at UTC millisecond precision with a UUID tie-break. Includes serially recovered archive messages. Pending scheduled messages are omitted until release. The default direction is older; without a cursor it reads the latest page. Newer requires a cursor and reads the nearest messages after that retained conversation-scoped anchor, without skipping to the latest page. Both directions return newest-first. nextCursor continues in the requested direction. Reads never fetch provider history or mark messages as read.",
        "responses": {
          "200": {
            "description": "Current resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagePage"
                },
                "example": {
                  "items": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "description": "Retained message UUID anchoring this conversation-scoped page at its current position. A schedule can move to a new position at release. Excludes the anchor. Use the oldest returned item for older and the newest returned item for newer. Required for newer.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "description": "Chronological direction from the retained cursor. Newer requires a cursor.",
            "schema": {
              "type": "string",
              "enum": [
                "older",
                "newer"
              ],
              "default": "older"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum page size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ]
      },
      "post": {
        "operationId": "sendMessage",
        "tags": [
          "Messages"
        ],
        "summary": "Accept an immediate or scheduled message",
        "description": "Atomically commits one logical message, operation receipt and outbox intent for the complete composition, including the ordered references to staged attachments. The engine checks the destination, reply, mentions, attachment scope and effective send capabilities before acceptance. Invalid or unavailable attachments reject the whole composition. Attachment bytes are staged locally and any provider upload belongs to dispatch. Room dispatch waits for confirmed membership under an active explicit join intent.\nOmitting scheduledAt makes the intent immediately eligible. A future schedule never blocks another eligible send. A missed schedule dispatches when the owning engine and destination become available again. Optional expectedReplyRevisionId and expectedReplyExcerptSha256 bind a new scheduled reply to the reviewed source and excerpt. A changed source rejects acceptance with state_conflict and replySourceChanged set to true. Idempotency includes the complete composition, normalized instant and any reply preconditions. A matching replay returns the original acceptance snapshot even after the scheduled time passes, attachment availability changes or the source is corrected. Different input with the same key yields idempotency_conflict. Read the message for current state. Ambiguous transmission remains unknown.",
        "responses": {
          "200": {
            "description": "Original acceptance replayed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "conversationId": "019a5df0-0000-7000-8000-000000000001",
                  "sender": {
                    "id": "019a5df0-0000-7000-8000-000000000001",
                    "displayName": "bob@localhost"
                  },
                  "originalText": "Hello from Oie!",
                  "createdAt": "2026-11-07T12:00:00Z",
                  "isOwn": false,
                  "sendState": "received"
                }
              }
            }
          },
          "202": {
            "headers": {
              "Location": {
                "description": "Accepted message; poll for current send state.",
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                }
              }
            },
            "description": "Message and send intent committed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "conversationId": "019a5df0-0000-7000-8000-000000000001",
                  "sender": {
                    "id": "019a5df0-0000-7000-8000-000000000001",
                    "displayName": "bob@localhost"
                  },
                  "originalText": "Hello from Oie!",
                  "createdAt": "2026-11-07T12:00:00Z",
                  "isOwn": false,
                  "sendState": "received"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "The request body exceeds the one MiB engine limit or an attachment exceeds the known provider upload limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable Idempotency-Key reference.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "X-Oie-Expected-Conversation-Kind",
            "in": "header",
            "description": "Conversation kind observed by the caller. A changed kind rejects a new send before acceptance.",
            "schema": {
              "type": "string",
              "enum": [
                "direct",
                "room"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Structured operation input.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMessage"
              },
              "examples": {
                "immediateText": {
                  "summary": "Existing immediate text send",
                  "value": {
                    "originalText": "Hello from Oie!"
                  }
                },
                "scheduledComposition": {
                  "summary": "Scheduled text with a link and a staged file",
                  "value": {
                    "originalText": "Studio entrance: https://example.org/studio",
                    "attachmentIds": [
                      "019a5df0-0000-7000-8000-000000000006"
                    ],
                    "scheduledAt": "2026-11-12T12:30:00-03:00"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/reactions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "get": {
        "operationId": "getMessageReactions",
        "tags": [
          "Messages"
        ],
        "summary": "Read retained reactions and current own mutation eligibility",
        "description": "Read-only canonical source snapshot. Missing revisions are null until an observed or submitted reaction set exists. Does not fetch provider data, mark read or allocate revisions.",
        "responses": {
          "200": {
            "description": "Scoped retained reaction snapshot",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageReactions"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "setMessageReactions",
        "tags": [
          "Messages"
        ],
        "summary": "Accept a durable replacement of the own reaction set",
        "description": "Persists a separate reaction command with an own-actor revision precondition. Empty emojis removes the own set. Other actors do not invalidate this precondition. At most one unresolved command per source is allowed. Submitted means written to the connection; unknown transmission is never blindly retried. Source content, activity, history, unread and original outbox stay unchanged.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable command UUID retained across retries and receipt lookup.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Complete desired own emoji set and observed own revision.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetMessageReactions"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing immutable command replayed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageReactionCommand"
                }
              }
            }
          },
          "202": {
            "headers": {
              "Location": {
                "description": "Accepted reaction command; poll for current dispatch state.",
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                }
              }
            },
            "description": "Durable reaction command committed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageReactionCommand"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/reactions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "listConversationReactions",
        "tags": [
          "Messages"
        ],
        "summary": "Read bounded reactions for the mounted conversation messages",
        "description": "All requested retained messages must belong to this conversation. Canonicalizes aliases and returns each canonical source once. Does not retrieve history or change read state.",
        "parameters": [
          {
            "name": "messageId",
            "in": "query",
            "required": true,
            "description": "Message UUIDs in the mounted bounded page.",
            "example": [
              "019a5df0-0000-7000-8000-000000000003"
            ],
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "minItems": 1,
              "maxItems": 100,
              "uniqueItems": true,
              "items": {
                "$ref": "#/components/schemas/ID"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scoped bounded reaction snapshots",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageReactionsList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/message-reaction-commands/{commandId}": {
      "parameters": [
        {
          "name": "commandId",
          "in": "path",
          "required": true,
          "description": "Reaction command UUID equal to its Idempotency-Key.",
          "schema": {
            "$ref": "#/components/schemas/ID"
          }
        }
      ],
      "get": {
        "operationId": "getMessageReactionCommand",
        "tags": [
          "Messages"
        ],
        "summary": "Read a durable reaction command receipt",
        "description": "Read-only reconciliation after lost responses. A missing receipt does not authorize a new key for the same uncertain intent. No provider call or dispatch is performed.",
        "responses": {
          "200": {
            "description": "Immutable reaction intent and actual transmission state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageReactionCommand"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/edit-target": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "get": {
        "operationId": "getMessageEditTarget",
        "tags": [
          "Messages"
        ],
        "summary": "Inspect eligibility and current content before editing",
        "description": "Read-only eligibility for the canonical message. Initially only the latest own plain text message sent by this engine in the current verified protocol session can be corrected. Reply and mention payloads remain ineligible. Eligibility is advisory and rechecked atomically at acceptance and dispatch. No arbitrary time window is imposed.",
        "responses": {
          "200": {
            "description": "Scoped current edit target and eligibility",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageEditTarget"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/edit-target": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        }
      ],
      "get": {
        "operationId": "getConversationEditTarget",
        "tags": [
          "Messages"
        ],
        "summary": "Inspect the latest own retained message for keyboard editing",
        "description": "Uses a durable transmission ordinal for original sends by this engine, independently of device clocks and the visible timeline page. Returns the latest own original transmission candidate even when it is ineligible; never silently skips it to edit an older one. Queued or cancelled originals which never reached dispatch, imported own messages and correction commands do not establish this order. Returns 404 when there is no retained engine transmission. Read-only and does not mark messages read or fetch provider history.",
        "responses": {
          "200": {
            "description": "Latest original engine transmission and current eligibility",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageEditTarget"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/edits": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "post": {
        "operationId": "editMessage",
        "tags": [
          "Messages"
        ],
        "summary": "Accept a durable correction of a sent message",
        "description": "Atomically persists a distinct correction command and operation receipt with an optimistic content revision precondition. The original send and content remain unchanged while queued or in flight. Submitted means written to the protocol connection, not confirmed recipient delivery. Unknown outcomes are not blindly retried. A frozen original sender wire ID is used for every correction. Read the command receipt after a lost response before considering another operation key.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable command UUID retained across retries and receipt lookup.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Corrected plain text and the exact observed content revision.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EditMessage"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same correction command was already accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageEditCommand"
                }
              }
            }
          },
          "202": {
            "headers": {
              "Location": {
                "description": "Accepted edit command; poll for current dispatch state.",
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                }
              }
            },
            "description": "Correction command committed without claiming delivery",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageEditCommand"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/message-edits/{editId}": {
      "parameters": [
        {
          "name": "editId",
          "in": "path",
          "required": true,
          "description": "Correction command UUID, equal to its submission Idempotency-Key.",
          "schema": {
            "$ref": "#/components/schemas/ID"
          }
        }
      ],
      "get": {
        "operationId": "getMessageEdit",
        "tags": [
          "Messages"
        ],
        "summary": "Reconcile a correction command by its stable UUID",
        "description": "Read-only durable receipt. A 404 does not authorize changing the key after an uncertain submission; repeat the same frozen request if needed. No provider calls or dispatch occur in this query.",
        "responses": {
          "200": {
            "description": "Retained correction command and actual dispatch state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageEditCommand"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/edit-history": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "get": {
        "operationId": "getMessageEditHistory",
        "tags": [
          "Messages"
        ],
        "summary": "Read observed content revisions of one retained message",
        "description": "Read-only bounded history of canonical content sources. Resolves retained aliases and excludes delivery-state revisions and unverified correction relationships. Versions never observed cannot be recovered by this query. Newest authored timestamp first with revision UUID tie-breaking; isCurrent is authoritative regardless of timestamp order. Cursor binds engine, account, conversation, canonical message and the exact observed-history snapshot. No provider history/profile/media requests, read receipts, sends or mutations occur.",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "description": "Maximum content revisions in this page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "description": "Opaque position bound to the exact retained-history snapshot and owning scopes.",
            "schema": {
              "type": "string",
              "maxLength": 2048
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Scoped retained content revisions and optional older-page cursor",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageEditHistoryPage"
                },
                "example": {
                  "requestedMessageId": "019a5df0-0000-7000-8000-000000000003",
                  "messageId": "019a5df0-0000-7000-8000-000000000003",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "conversationId": "019a5df0-0000-7000-8000-000000000002",
                  "currentRevisionId": "019a5df0-0000-7000-8000-000000000004",
                  "snapshot": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                  "items": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Observed content history changed; restart at the first page",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                },
                "example": {
                  "type": "urn:oie:problem:history_changed",
                  "title": "History changed",
                  "status": 409,
                  "code": "history_changed",
                  "documentation": "https://github.com/araihu/oie/blob/main/docs/engine-api-problems.md#history_changed"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/sender-profile": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "get": {
        "operationId": "getMessageSenderProfile",
        "tags": [
          "Messages"
        ],
        "summary": "Read the author's available published profile",
        "description": "Resolves the author from the retained message and message-time protocol identity evidence. May issue a bounded read-only profile query to a known personal address. Room nicknames and current same-name occupants never establish a historical author's personal JID. Missing or undisclosed identity remains unavailable; user-provided vCard JABBERID does not establish it. Does not join rooms, add contacts, subscribe to presence or mark messages read.",
        "responses": {
          "200": {
            "description": "Scoped profile availability and optional published full name and personal address",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageSenderProfile"
                },
                "example": {
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "conversationId": "019a5df0-0000-7000-8000-000000000002",
                  "messageId": "019a5df0-0000-7000-8000-000000000003",
                  "state": "not_disclosed"
                }
              }
            }
          },
          "400": {
            "description": "Invalid message identifier",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "description": "Engine state is temporarily unavailable.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/messages/{messageId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "get": {
        "operationId": "getMessage",
        "tags": [
          "Messages"
        ],
        "summary": "Read a retained message",
        "description": "Returns the latest revision and send evidence. A retained correction-source alias resolves to its canonical logical message, whose id can differ from the requested messageId. Use the scoped context operation when explicit requestedTargetId alias proof is needed.",
        "responses": {
          "200": {
            "description": "Current resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "conversationId": "019a5df0-0000-7000-8000-000000000001",
                  "sender": {
                    "id": "019a5df0-0000-7000-8000-000000000001",
                    "displayName": "bob@localhost"
                  },
                  "originalText": "Hello from Oie!",
                  "createdAt": "2026-11-07T12:00:00Z",
                  "isOwn": false,
                  "sendState": "received"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "post": {
        "operationId": "cancelMessage",
        "tags": [
          "Messages"
        ],
        "summary": "Cancel a pending send",
        "description": "Requires the current revision and a queued, known-unsent message at the owning engine, including a future schedule. Cancellation and dispatch share an atomic cutoff before any external provider attempt for that intent, including attachment upload. A stale revision or ineligible state yields state_conflict with currentMessage. Cancellation retires the send intent and preserves any independent retained references. A matching idempotent replay returns the original cancellation snapshot.",
        "responses": {
          "200": {
            "description": "Current resource",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "revisionId": "019a5df0-0000-7000-8000-000000000001",
                  "accountId": "019a5df0-0000-7000-8000-000000000001",
                  "conversationId": "019a5df0-0000-7000-8000-000000000001",
                  "sender": {
                    "id": "019a5df0-0000-7000-8000-000000000001",
                    "displayName": "bob@localhost"
                  },
                  "originalText": "Hello from Oie!",
                  "createdAt": "2026-11-07T12:00:00Z",
                  "isOwn": false,
                  "sendState": "received"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "Request body exceeds the one MiB engine limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Request media type is not supported.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable Idempotency-Key reference.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Structured operation input.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelMessage"
              },
              "example": {
                "expectedRevisionId": "019a5df0-0000-7000-8000-000000000001"
              }
            }
          }
        }
      }
    },
    "/api/v1/conversations/{conversationId}/staged-attachments/{attachmentId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ConversationID"
        },
        {
          "$ref": "#/components/parameters/StagedAttachmentID"
        }
      ],
      "put": {
        "tags": [
          "Messages"
        ],
        "operationId": "stageMessageAttachment",
        "summary": "Stage a file for an outgoing message",
        "description": "Store one complete file durably on the owning engine, scoped to this conversation and its account. The client assigns the ID once. A matching repeat with the same file bytes and filename returns the original receipt. Different input for that ID yields idempotency_conflict. An incomplete request leaves no usable attachment. Confirmed local staging performs no provider upload. Send and pending edit accept only available attachments in the same scope. The engine retains accepted references while their send is unresolved, including through restart, rescheduling and Send now.",
        "requestBody": {
          "required": true,
          "description": "One file with its filename in the file part's Content-Disposition.",
          "content": {
            "multipart/form-data": {
              "example": {
                "file": "hello"
              },
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "example": "hello"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matching staged attachment receipt replayed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StagedAttachment"
                }
              }
            }
          },
          "201": {
            "description": "File durably staged",
            "headers": {
              "Location": {
                "description": "Scoped URL for the staged bytes.",
                "schema": {
                  "type": "string",
                  "format": "uri-reference"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StagedAttachment"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "description": "File exceeds the engine's attachment staging limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "415": {
            "description": "Unsupported request media type or attachment format.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "get": {
        "tags": [
          "Messages"
        ],
        "operationId": "getStagedMessageAttachment",
        "summary": "Read retained bytes of an outgoing attachment",
        "description": "Reads local bytes in this conversation and account scope, for example when reopening an existing scheduled composition. Returns 404 when bytes are unavailable or the attachment is outside this scope.",
        "responses": {
          "200": {
            "description": "Retained attachment bytes",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/scheduled-messages": {
      "get": {
        "tags": [
          "Messages"
        ],
        "operationId": "listScheduledMessages",
        "summary": "List scheduled messages across conversations",
        "description": "Lists the selected engine's own queued, known-unsent messages with a configured schedule, including overdue sends waiting for connectivity. Results include their destination. Successful Send now removes the item from this list even if connectivity delays dispatch. Results sort by scheduledAt ascending, then message UUID ascending. Pagination is over current data. Concurrent changes can move items across the cursor boundary.",
        "parameters": [
          {
            "name": "conversationId",
            "in": "query",
            "description": "Limit results to one conversation on this engine. Cannot be combined with conversationIds.",
            "schema": {
              "$ref": "#/components/schemas/ID"
            }
          },
          {
            "name": "conversationIds",
            "in": "query",
            "description": "Limit results to any of the selected conversations on this engine. Cannot be combined with conversationId. Order does not affect results or continuation.",
            "style": "form",
            "explode": true,
            "example": [
              "019a5df0-0000-7000-8000-000000000001",
              "019a5df0-0000-7000-8000-000000000002"
            ],
            "schema": {
              "type": "array",
              "minItems": 1,
              "maxItems": 200,
              "uniqueItems": true,
              "items": {
                "$ref": "#/components/schemas/ID"
              }
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "description": "Opaque continuation bound to this engine and conversation filter.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 8192
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Maximum number of scheduled messages in this page.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of pending scheduled messages",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduledMessagePage"
                },
                "example": {
                  "engineId": "019a5df0-0000-7000-8000-000000000001",
                  "items": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/pending": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "patch": {
        "tags": [
          "Messages"
        ],
        "operationId": "updatePendingMessage",
        "summary": "Edit or reschedule a pending message",
        "description": "Replace the full composition, change the schedule, or do both in one transaction at the owning engine. It must be an own queued, known-unsent message with the expected revision. The message ID, destination and creation time are retained. The engine validates new or changed reply, mentions and attachment references, then commits the updated composition and attachment references together. A schedule-only update preserves the accepted composition.\nThe mutation and the dispatch claim share an atomic cutoff before any external provider attempt for this intent, including attachment upload. Stale revision, ineligible state or a winning dispatch claim yields state_conflict with currentMessage. When the mutation wins, a later claim uses the new revision and eligibility time. Matching idempotent replay returns the original result before evaluating current state, attachment availability or time preconditions.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ScheduledMutationKey"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Expected revision and the fields to change.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdatePendingMessage"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/PendingMessageUpdated"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/SchedulingBodyTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/SchedulingUnsupportedMedia"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/v1/messages/{messageId}/send-now": {
      "parameters": [
        {
          "$ref": "#/components/parameters/MessageID"
        }
      ],
      "post": {
        "tags": [
          "Messages"
        ],
        "operationId": "sendScheduledMessageNow",
        "summary": "Make a scheduled message eligible immediately",
        "description": "With the current expected revision, atomically clears scheduledAt on the same queued, known-unsent scheduled intent and makes it eligible immediately. The message ID, destination, composition and attachments remain attached to the same intent. Acceptance records eligibility. Dispatch still requires the owning engine, connectivity and any existing destination prerequisites. This operation and dispatch share the same atomic cutoff as pending edit and cancel, including before provider attachment upload. A stale revision, ineligible state or winning dispatch claim yields state_conflict with currentMessage. Repeating the same key and input returns the original successful result even if dispatch has since started.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ScheduledMutationKey"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Expected revision of the scheduled message to send now.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendScheduledMessageNow"
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/PendingMessageUpdated"
          },
          "400": {
            "$ref": "#/components/responses/Invalid"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/SchedulingBodyTooLarge"
          },
          "415": {
            "$ref": "#/components/responses/SchedulingUnsupportedMedia"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "SearchAccountIDs": {
        "name": "accountIds",
        "in": "query",
        "style": "form",
        "explode": true,
        "description": "Restrict results to the selected owning accounts before pagination. Mutually exclusive with accountId.",
        "example": [
          "019a5df0-0000-7000-8000-000000000001",
          "019a5df0-0000-7000-8000-000000000002"
        ],
        "schema": {
          "type": "array",
          "minItems": 1,
          "maxItems": 100,
          "uniqueItems": true,
          "items": {
            "$ref": "#/components/schemas/ID"
          }
        }
      },
      "AccountID": {
        "name": "accountId",
        "in": "path",
        "required": true,
        "description": "Stable account UUIDv7.",
        "schema": {
          "$ref": "#/components/schemas/ID"
        }
      },
      "ConversationID": {
        "name": "conversationId",
        "in": "path",
        "required": true,
        "description": "Stable conversationId UUIDv7.",
        "schema": {
          "$ref": "#/components/schemas/ID"
        }
      },
      "MessageID": {
        "name": "messageId",
        "in": "path",
        "required": true,
        "description": "Stable messageId UUIDv7.",
        "schema": {
          "$ref": "#/components/schemas/ID"
        }
      },
      "StagedAttachmentID": {
        "name": "attachmentId",
        "in": "path",
        "required": true,
        "description": "Client-assigned UUIDv7 of one staged attachment.",
        "schema": {
          "$ref": "#/components/schemas/ID"
        }
      },
      "ScheduledMutationKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Stable mutation key. Different input with the same key yields idempotency_conflict.",
        "schema": {
          "$ref": "#/components/schemas/ID"
        }
      }
    },
    "responses": {
      "Invalid": {
        "description": "Invalid request",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "urn:oie:problem:invalid_request",
              "title": "Invalid request",
              "status": 400,
              "code": "invalid_request",
              "documentation": "https://github.com/araihu/oie/blob/main/docs/engine-api-problems.md#invalid_request"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "urn:oie:problem:not_found",
              "title": "Not Found",
              "status": 404,
              "code": "not_found",
              "documentation": "https://github.com/araihu/oie/blob/main/docs/engine-api-problems.md#not_found"
            }
          }
        }
      },
      "Conflict": {
        "description": "Idempotency conflict, duplicate address, invalid stream cursor, or a message or reply source conflict",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "urn:oie:problem:idempotency_conflict",
              "title": "Idempotency key conflict",
              "status": 409,
              "code": "idempotency_conflict",
              "documentation": "https://github.com/araihu/oie/blob/main/docs/engine-api-problems.md#idempotency_conflict"
            }
          }
        }
      },
      "Unavailable": {
        "description": "Engine persistence or protocol service temporarily unavailable",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            },
            "example": {
              "type": "urn:oie:problem:unavailable",
              "title": "Service Unavailable",
              "status": 503,
              "code": "unavailable",
              "documentation": "https://github.com/araihu/oie/blob/main/docs/engine-api-problems.md#unavailable"
            }
          }
        }
      },
      "PendingMessageUpdated": {
        "description": "Committed message revision or original successful replay",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Message"
            }
          }
        }
      },
      "SchedulingBodyTooLarge": {
        "description": "JSON mutation exceeds the one MiB engine request limit or a pending composition edit includes an attachment exceeding the known provider upload limit.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      },
      "SchedulingUnsupportedMedia": {
        "description": "JSON mutation requires application/json.",
        "content": {
          "application/problem+json": {
            "schema": {
              "$ref": "#/components/schemas/Problem"
            }
          }
        }
      }
    },
    "schemas": {
      "ID": {
        "type": "string",
        "format": "uuid",
        "x-go-type": "string",
        "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
        "example": "019a5df0-0000-7000-8000-000000000001",
        "description": "Canonical lowercase UUIDv7; IDs do not establish causal order."
      },
      "NullableRevisionID": {
        "type": "string",
        "format": "uuid",
        "x-go-type": "string",
        "nullable": true,
        "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
        "example": "019a5df0-0000-7000-8000-000000000004",
        "description": "Canonical UUIDv7 of app-owned state; null means no revision has been created."
      },
      "MarkMessagesRead": {
        "description": "Exact message identities observed by the requesting client.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "messageIds"
        ],
        "properties": {
          "messageIds": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "uniqueItems": true,
            "example": [
              "019a5df0-0000-7000-8000-000000000004"
            ],
            "items": {
              "$ref": "#/components/schemas/ID"
            }
          }
        },
        "example": {
          "messageIds": [
            "019a5df0-0000-7000-8000-000000000004"
          ]
        }
      },
      "ConversationReadState": {
        "description": "Durable app-owned read revision with the current retained unread count.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "conversationId",
          "accountId",
          "revisionId",
          "updatedAt",
          "unreadCount"
        ],
        "properties": {
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/NullableRevisionID"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "unreadCount": {
            "type": "integer",
            "minimum": 0
          }
        },
        "example": {
          "conversationId": "019a5df0-0000-7000-8000-000000000003",
          "accountId": "019a5df0-0000-7000-8000-000000000002",
          "revisionId": null,
          "updatedAt": null,
          "unreadCount": 3
        }
      },
      "MessageStar": {
        "type": "object",
        "description": "App-owned source message star, keyed by messageId with its own revision history. Never a message edit.",
        "additionalProperties": false,
        "required": [
          "messageId",
          "accountId",
          "conversationId",
          "revisionId",
          "starred",
          "updatedAt"
        ],
        "properties": {
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/NullableRevisionID"
          },
          "starred": {
            "type": "boolean",
            "example": true,
            "description": "Whether the owner starred the source message, independently of any attachment."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "example": "2026-11-08T12:00:00Z",
            "description": "UTC timestamp of the latest star change, null before the first change."
          }
        },
        "example": {
          "messageId": "019a5df0-0000-7000-8000-000000000001",
          "accountId": "019a5df0-0000-7000-8000-000000000002",
          "conversationId": "019a5df0-0000-7000-8000-000000000003",
          "revisionId": null,
          "starred": false,
          "updatedAt": null
        }
      },
      "SetMessageStar": {
        "type": "object",
        "description": "Desired star state and mandatory optimistic precondition; use a new operation UUID for a new decision.",
        "additionalProperties": false,
        "required": [
          "starred",
          "expectedRevisionId"
        ],
        "properties": {
          "starred": {
            "type": "boolean",
            "description": "Desired value, never an ambiguous toggle."
          },
          "expectedRevisionId": {
            "$ref": "#/components/schemas/NullableRevisionID"
          }
        },
        "example": {
          "starred": true,
          "expectedRevisionId": null
        }
      },
      "MessageStarPage": {
        "type": "object",
        "example": {
          "items": [
            {
              "messageId": "019a5df0-0000-7000-8000-000000000001",
              "accountId": "019a5df0-0000-7000-8000-000000000002",
              "conversationId": "019a5df0-0000-7000-8000-000000000003",
              "revisionId": null,
              "starred": false,
              "updatedAt": null
            }
          ]
        },
        "description": "Bounded states for the requested source message UUIDs, including untouched initial state.",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MessageStar"
            }
          }
        }
      },
      "StarredMessage": {
        "type": "object",
        "example": {
          "message": {
            "id": "019a5df0-0000-7000-8000-000000000001",
            "revisionId": "019a5df0-0000-7000-8000-000000000001",
            "accountId": "019a5df0-0000-7000-8000-000000000001",
            "conversationId": "019a5df0-0000-7000-8000-000000000001",
            "sender": {
              "id": "019a5df0-0000-7000-8000-000000000001",
              "displayName": "bob@localhost"
            },
            "originalText": "Hello from Oie!",
            "createdAt": "2026-11-07T12:00:00Z",
            "isOwn": false,
            "sendState": "received"
          },
          "conversation": {
            "id": "019a5df0-0000-7000-8000-000000000001",
            "revisionId": "019a5df0-0000-7000-8000-000000000001",
            "accountId": "019a5df0-0000-7000-8000-000000000001",
            "kind": "direct",
            "address": "bob@localhost",
            "title": "bob@localhost",
            "lastMessage": "Hello from Oie!",
            "updatedAt": "2026-11-07T12:00:00Z",
            "unreadCount": 0,
            "archived": false,
            "favorite": false
          },
          "star": {
            "messageId": "019a5df0-0000-7000-8000-000000000001",
            "accountId": "019a5df0-0000-7000-8000-000000000002",
            "conversationId": "019a5df0-0000-7000-8000-000000000003",
            "revisionId": "019a5df0-0000-7000-8000-000000000004",
            "starred": true,
            "updatedAt": "2026-11-08T12:00:00Z"
          }
        },
        "description": "Latest source message and conversation projections with their independent star state.",
        "additionalProperties": false,
        "required": [
          "message",
          "conversation",
          "star"
        ],
        "properties": {
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "conversation": {
            "$ref": "#/components/schemas/Conversation"
          },
          "star": {
            "$ref": "#/components/schemas/MessageStar"
          }
        }
      },
      "StarredMessagePage": {
        "type": "object",
        "example": {
          "items": [
            {
              "message": {
                "id": "019a5df0-0000-7000-8000-000000000001",
                "revisionId": "019a5df0-0000-7000-8000-000000000001",
                "accountId": "019a5df0-0000-7000-8000-000000000001",
                "conversationId": "019a5df0-0000-7000-8000-000000000001",
                "sender": {
                  "id": "019a5df0-0000-7000-8000-000000000001",
                  "displayName": "bob@localhost"
                },
                "originalText": "Hello from Oie!",
                "createdAt": "2026-11-07T12:00:00Z",
                "isOwn": false,
                "sendState": "received"
              },
              "conversation": {
                "id": "019a5df0-0000-7000-8000-000000000001",
                "revisionId": "019a5df0-0000-7000-8000-000000000001",
                "accountId": "019a5df0-0000-7000-8000-000000000001",
                "kind": "direct",
                "address": "bob@localhost",
                "title": "bob@localhost",
                "lastMessage": "Hello from Oie!",
                "updatedAt": "2026-11-07T12:00:00Z",
                "unreadCount": 0,
                "archived": false,
                "favorite": false
              },
              "star": {
                "messageId": "019a5df0-0000-7000-8000-000000000001",
                "accountId": "019a5df0-0000-7000-8000-000000000002",
                "conversationId": "019a5df0-0000-7000-8000-000000000003",
                "revisionId": "019a5df0-0000-7000-8000-000000000004",
                "starred": true,
                "updatedAt": "2026-11-08T12:00:00Z"
              }
            }
          ]
        },
        "description": "Chronologically paginated current starred messages. Newly starred records can appear on earlier pages.",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StarredMessage"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/ID"
          }
        }
      },
      "Health": {
        "type": "object",
        "description": "Readiness of the engine store.",
        "example": {
          "status": "ready"
        },
        "additionalProperties": false,
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ready"
            ],
            "example": "ready"
          }
        }
      },
      "Engine": {
        "type": "object",
        "description": "Persistent engine identity and event boundary.",
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000003",
          "role": "local",
          "apiVersion": "0.30.0",
          "eventCursor": "019a5df0-0000-7000-8000-000000000003:1"
        },
        "additionalProperties": false,
        "required": [
          "id",
          "role",
          "apiVersion",
          "eventCursor"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "role": {
            "type": "string",
            "enum": [
              "local",
              "hosted"
            ]
          },
          "apiVersion": {
            "type": "string",
            "description": "Engine contract revision, independent of URL major and application release.",
            "pattern": "^\\d+\\.\\d+\\.\\d+$",
            "enum": [
              "0.11.0",
              "0.12.0",
              "0.29.0",
              "0.30.0"
            ]
          },
          "eventCursor": {
            "type": "string",
            "description": "Application stream boundary, separate from provider history cursors."
          },
          "canResetData": {
            "type": "boolean",
            "description": "This engine supports deleting its data through the reset operation."
          }
        }
      },
      "RegisterAccount": {
        "type": "object",
        "description": "Account metadata registration, without protocol credentials.",
        "example": {
          "protocol": "xmpp",
          "address": "alice@localhost",
          "label": "Personal"
        },
        "additionalProperties": false,
        "required": [
          "protocol",
          "address",
          "label"
        ],
        "properties": {
          "protocol": {
            "type": "string",
            "enum": [
              "xmpp"
            ]
          },
          "address": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1023
          },
          "label": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          }
        }
      },
      "Account": {
        "type": "object",
        "description": "Stable account identity and its current initial revision.",
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000001",
          "revisionId": "019a5df0-0000-7000-8000-000000000002",
          "protocol": "xmpp",
          "address": "alice@localhost",
          "label": "Personal",
          "connectionState": "not_connected",
          "createdAt": "2026-11-07T12:00:00Z"
        },
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "protocol",
          "address",
          "label",
          "connectionState",
          "createdAt"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "xmpp"
            ]
          },
          "address": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "connectionState": {
            "type": "string",
            "enum": [
              "not_connected",
              "connecting",
              "connected",
              "error"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AccountPage": {
        "type": "object",
        "description": "A keyset-paginated account collection.",
        "example": {
          "items": [
            {
              "id": "019a5df0-0000-7000-8000-000000000001",
              "revisionId": "019a5df0-0000-7000-8000-000000000002",
              "protocol": "xmpp",
              "address": "alice@localhost",
              "label": "Personal",
              "connectionState": "not_connected",
              "createdAt": "2026-11-07T12:00:00Z"
            }
          ]
        },
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Account"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/ID"
          }
        }
      },
      "Capabilities": {
        "type": "object",
        "description": "Implementation and discovered feature evidence scoped to one account.",
        "example": {
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "state": "unknown",
          "reason": "not_connected",
          "features": [],
          "discovery": {
            "state": "not_connected",
            "services": []
          }
        },
        "additionalProperties": false,
        "required": [
          "accountId",
          "state",
          "reason",
          "features",
          "discovery"
        ],
        "properties": {
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "state": {
            "type": "string",
            "enum": [
              "unknown",
              "known"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "not_connected",
              "adapter_connected"
            ]
          },
          "features": {
            "type": "array",
            "maxItems": 64,
            "items": {
              "$ref": "#/components/schemas/CapabilityFeature"
            },
            "description": "Stable app feature rows. These diagnostics never authorize an operation."
          },
          "discovery": {
            "$ref": "#/components/schemas/CapabilityDiscovery"
          },
          "directText": {
            "type": "boolean",
            "description": "True only while the basic direct-text adapter is connected. No other optional protocol support is implied."
          }
        }
      },
      "SupportState": {
        "type": "string",
        "enum": [
          "supported",
          "partial",
          "unsupported",
          "unknown",
          "not_required",
          "scope_dependent",
          "not_advertised"
        ],
        "description": "App unsupported means not implemented. Protocol supported means defined by that protocol, independently of Oie implementation. Server supported means observed advertised evidence. not_advertised means the relevant successful query omitted that feature, not a definitive operational denial. scope_dependent needs peer or room evidence. Unknown is never treated as false."
      },
      "CapabilityFeature": {
        "description": "One feature compared across Oie implementation, protocol definition and scoped server evidence.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "appSupport",
          "protocolSupport",
          "serverSupport",
          "scope"
        ],
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "direct_text",
              "account_history",
              "saved_rooms",
              "room_history",
              "room_discovery",
              "room_messaging",
              "file_upload",
              "image_previews",
              "link_previews",
              "received_replies",
              "send_replies",
              "mentions",
              "edit_messages",
              "send_corrections",
              "retract_messages",
              "reactions",
              "read_receipts",
              "typing",
              "presence",
              "profile_images",
              "formatting",
              "e2ee",
              "message_stars",
              "message_search",
              "schedules",
              "sync"
            ]
          },
          "appSupport": {
            "$ref": "#/components/schemas/SupportState"
          },
          "protocolSupport": {
            "$ref": "#/components/schemas/SupportState"
          },
          "serverSupport": {
            "$ref": "#/components/schemas/SupportState"
          },
          "scope": {
            "type": "string",
            "enum": [
              "local",
              "account",
              "server",
              "service",
              "peer",
              "room"
            ]
          }
        }
      },
      "CapabilityDiscovery": {
        "description": "Session-local discovery status and the bounded entity observations collected so far.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "state",
          "services"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "not_connected",
              "pending",
              "complete",
              "partial",
              "failed"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC observation time, absent before an attempt completes."
          },
          "services": {
            "type": "array",
            "maxItems": 32,
            "items": {
              "$ref": "#/components/schemas/DiscoveredService"
            },
            "description": "Bounded discovered entities, not a complete server inventory. Service discovery skips item nodes and foreign account domains. Room evidence comes only from authorized saved-room history discovery. No room join."
          }
        }
      },
      "DiscoveredService": {
        "description": "A validated entity response with advertised namespaces, identities and observation state.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "address",
          "scope",
          "state",
          "features",
          "identities",
          "truncated"
        ],
        "properties": {
          "address": {
            "type": "string",
            "maxLength": 1024
          },
          "scope": {
            "type": "string",
            "enum": [
              "account",
              "server",
              "service",
              "room"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "known",
              "failed"
            ]
          },
          "features": {
            "type": "array",
            "maxItems": 256,
            "items": {
              "type": "string",
              "maxLength": 1024
            }
          },
          "identities": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "category",
                "type"
              ],
              "properties": {
                "category": {
                  "type": "string",
                  "maxLength": 300
                },
                "type": {
                  "type": "string",
                  "maxLength": 300
                },
                "name": {
                  "type": "string",
                  "maxLength": 300
                }
              }
            }
          },
          "truncated": {
            "type": "boolean",
            "description": "Bounded or invalid evidence was omitted; absent features remain unknown."
          },
          "errorCode": {
            "type": "string",
            "enum": [
              "discovery_failed"
            ]
          }
        }
      },
      "Operation": {
        "type": "object",
        "description": "Durable receipt used to reconcile repeated mutations.",
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000004",
          "kind": "register_account",
          "status": "succeeded",
          "resourceId": "019a5df0-0000-7000-8000-000000000001",
          "createdAt": "2026-11-07T12:00:00Z"
        },
        "additionalProperties": false,
        "required": [
          "id",
          "kind",
          "status",
          "resourceId",
          "createdAt"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "kind": {
            "type": "string",
            "enum": [
              "register_account",
              "open_conversation",
              "update_conversation_preferences",
              "mark_messages_read",
              "send_message",
              "cancel_message",
              "set_message_star",
              "join_room",
              "leave_room"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded"
            ]
          },
          "resourceId": {
            "$ref": "#/components/schemas/ID"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "acceptedMessage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Message"
              }
            ],
            "description": "Immutable original message acceptance, available only when requested for a send_message receipt. Read the message resource separately for its current state."
          }
        }
      },
      "Event": {
        "type": "object",
        "description": "Durable event with the payload corresponding to its type. Accounts, connections, conversations, and messages are separate payloads. Payloads are snapshots at the event time. Refresh affected retained pages after message or conversation updates; validated corrections can collapse previously separate source UUIDs into one logical message. Historical event snapshots do not replace the current canonical projection. No credentials or raw protocol errors are included.",
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000003:1",
          "type": "account.registered",
          "occurredAt": "2026-11-07T12:00:00Z",
          "account": {
            "id": "019a5df0-0000-7000-8000-000000000001",
            "revisionId": "019a5df0-0000-7000-8000-000000000002",
            "protocol": "xmpp",
            "address": "alice@localhost",
            "label": "Personal",
            "connectionState": "not_connected",
            "createdAt": "2026-11-07T12:00:00Z"
          }
        },
        "additionalProperties": false,
        "required": [
          "id",
          "type",
          "occurredAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Engine-scoped durable event cursor."
          },
          "type": {
            "type": "string",
            "enum": [
              "account.registered",
              "account.connection_changed",
              "account.history_changed",
              "profile.avatar_changed",
              "message.preview_changed",
              "message.star_changed",
              "conversation.created",
              "conversation.updated",
              "conversation.room_session_changed",
              "message.created",
              "message.updated"
            ]
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time"
          },
          "account": {
            "$ref": "#/components/schemas/Account"
          },
          "conversation": {
            "$ref": "#/components/schemas/Conversation"
          },
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "connection": {
            "$ref": "#/components/schemas/Connection"
          },
          "history": {
            "$ref": "#/components/schemas/HistoryStatus"
          },
          "avatar": {
            "$ref": "#/components/schemas/Avatar"
          },
          "star": {
            "$ref": "#/components/schemas/MessageStar"
          },
          "preview": {
            "$ref": "#/components/schemas/MessagePreview"
          },
          "roomSession": {
            "$ref": "#/components/schemas/RoomSession"
          }
        }
      },
      "Problem": {
        "type": "object",
        "example": {
          "type": "urn:oie:problem:unavailable",
          "title": "Service Unavailable",
          "status": 503,
          "code": "unavailable",
          "documentation": "https://github.com/araihu/oie/blob/main/docs/engine-api-problems.md#unavailable"
        },
        "additionalProperties": false,
        "description": "RFC 9457 Problem Details served as application/problem+json. The type URI identifies the problem. The code extension is a stable short identifier for display and localization. The Link response header with rel=\"help\" provides guidance for known problem types. The body includes documentation when the request applies the oie-problem-documentation preference. Responses exclude raw storage errors and credentials.",
        "externalDocs": {
          "description": "RFC 9457 Problem Details for HTTP APIs",
          "url": "https://www.rfc-editor.org/rfc/rfc9457.html"
        },
        "required": [
          "type",
          "title",
          "status",
          "code"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "Stable absolute problem type URI in the form urn:oie:problem:<code>."
          },
          "title": {
            "type": "string",
            "description": "Short human-readable summary that is stable for a problem type."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code of this response."
          },
          "code": {
            "type": "string",
            "description": "Oiê extension for the short problem code.",
            "enum": [
              "invalid_request",
              "not_found",
              "idempotency_conflict",
              "account_exists",
              "invalid_cursor",
              "history_changed",
              "unavailable",
              "state_conflict",
              "protocol_unavailable",
              "media_blocked",
              "media_fetch_failed",
              "media_too_large",
              "media_unsupported",
              "range_not_satisfiable",
              "media_busy",
              "media_timeout"
            ]
          },
          "documentation": {
            "type": "string",
            "format": "uri",
            "description": "Absolute HTTPS link to human-readable guidance for this problem type. Included when Prefer requests oie-problem-documentation with no value or an empty quoted value. The URL may change independently of the type URI and code."
          },
          "currentConversation": {
            "$ref": "#/components/schemas/Conversation"
          },
          "currentStar": {
            "$ref": "#/components/schemas/MessageStar"
          },
          "currentMessage": {
            "$ref": "#/components/schemas/Message"
          },
          "currentRoomSession": {
            "$ref": "#/components/schemas/RoomSession"
          },
          "roomAddressInUse": {
            "type": "boolean",
            "description": "The address has a direct conversation with retained content that cannot be converted to a room."
          },
          "replySourceChanged": {
            "type": "boolean",
            "description": "True on a state conflict caused by an unavailable or changed reply source or excerpt when the request included expectedReplyRevisionId."
          }
        }
      },
      "Connection": {
        "type": "object",
        "description": "Current owning-engine connection state. Contains no credentials.",
        "additionalProperties": false,
        "required": [
          "accountId",
          "state",
          "updatedAt"
        ],
        "properties": {
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "state": {
            "type": "string",
            "description": "Runtime connection state.",
            "enum": [
              "not_connected",
              "connecting",
              "connected",
              "error"
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC timestamp."
          },
          "lastErrorCode": {
            "type": "string",
            "description": "Stable localized-error key; raw provider errors are never returned.",
            "enum": [
              "authentication_failed",
              "tls_failed",
              "connection_failed"
            ]
          }
        },
        "example": {
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "state": "not_connected",
          "updatedAt": "2026-11-07T12:00:00Z"
        }
      },
      "DraftLinkPreviewRequest": {
        "type": "object",
        "description": "Public HTTP or HTTPS URL detected in a draft.",
        "additionalProperties": false,
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "example": "https://example.com/studio"
          }
        }
      },
      "DraftLinkPreview": {
        "type": "object",
        "description": "Transient link metadata for a draft composition.",
        "additionalProperties": false,
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "example": "https://example.com/studio"
          },
          "title": {
            "type": "string",
            "maxLength": 300,
            "example": "Studio"
          },
          "description": {
            "type": "string",
            "maxLength": 1000,
            "example": "Opening hours and directions."
          },
          "image": {
            "$ref": "#/components/schemas/DraftLinkPreviewImage"
          }
        }
      },
      "DraftLinkPreviewImage": {
        "type": "object",
        "description": "Optional bounded raster image for a draft link preview.",
        "additionalProperties": false,
        "required": [
          "mediaType",
          "data"
        ],
        "properties": {
          "mediaType": {
            "type": "string",
            "enum": [
              "image/png",
              "image/jpeg",
              "image/gif"
            ],
            "example": "image/png"
          },
          "data": {
            "type": "string",
            "maxLength": 174764,
            "description": "Base64 of at most 128 KiB of raster image bytes.",
            "example": "iVBORw0KGgo="
          }
        }
      },
      "MessagePreview": {
        "type": "object",
        "description": "Source-linked cached metadata. Entity UUID remains stable as enrichment/download revisions change. An absent imageHash means no local thumbnail is available. Image references without a thumbnail require explicit original download before preview/expansion.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "accountId",
          "conversationId",
          "messageId",
          "url",
          "kind",
          "state",
          "downloadState",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "Original public HTTP or HTTPS message URL. Never use this as a browser image source."
          },
          "kind": {
            "type": "string",
            "enum": [
              "link",
              "image"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "ready",
              "unavailable"
            ]
          },
          "downloadState": {
            "type": "string",
            "enum": [
              "not_downloaded",
              "queued",
              "downloading",
              "ready",
              "failed"
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "title": {
            "type": "string",
            "maxLength": 300,
            "description": "Plain page title or image filename."
          },
          "description": {
            "type": "string",
            "maxLength": 1000,
            "description": "Plain page description."
          },
          "siteName": {
            "type": "string",
            "maxLength": 200,
            "description": "Plain publisher name or hostname."
          },
          "lastErrorCode": {
            "type": "string",
            "maxLength": 40,
            "description": "Stable failure code for localization, without provider error details."
          },
          "imageHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Local static PNG or JPEG thumbnail digest, sized for high-DPI message cards."
          },
          "faviconHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Optional locally cached and normalized site icon. Never a provider URL."
          },
          "fileHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Retained original image digest, computed after complete download."
          },
          "fileMimeType": {
            "type": "string",
            "enum": [
              "image/png",
              "image/jpeg",
              "image/gif"
            ]
          },
          "fileSize": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "maximum": 33554432
          },
          "integrity": {
            "type": "string",
            "enum": [
              "computed_only"
            ],
            "description": "No expected sender digest was supplied for this URL reference."
          }
        },
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000001",
          "revisionId": "019a5df0-0000-7000-8000-000000000001",
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "conversationId": "019a5df0-0000-7000-8000-000000000001",
          "messageId": "019a5df0-0000-7000-8000-000000000001",
          "url": "https://example.com/article",
          "kind": "link",
          "state": "ready",
          "downloadState": "not_downloaded",
          "title": "Shared article",
          "siteName": "Example",
          "updatedAt": "2026-11-08T12:00:00Z"
        }
      },
      "VideoThumbnail": {
        "type": "object",
        "description": "Cached frame metadata for one canonical retained video reference. A ready imageHash is a normalized static thumbnail; original video bytes are never retained by this operation.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "sharedContentId",
          "accountId",
          "conversationId",
          "messageId",
          "url",
          "kind",
          "state",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "sharedContentId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "Retained source reference for identity checks, never an image source.",
            "example": "https://files.example/clip.mp4"
          },
          "kind": {
            "type": "string",
            "enum": [
              "video"
            ],
            "example": "video"
          },
          "state": {
            "type": "string",
            "enum": [
              "not_requested",
              "queued",
              "ready",
              "unavailable"
            ],
            "example": "queued"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-08T12:00:00Z"
          },
          "imageHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Local normalized PNG or JPEG digest for a ready thumbnail.",
            "example": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
          },
          "lastErrorCode": {
            "type": "string",
            "maxLength": 40,
            "description": "Stable failure key. Failed extraction is retried only on explicit request.",
            "example": "frame_unavailable"
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "A retained protocol roster entry, separate from inferred peers, room occupants and future app-owned identity linking.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "accountId",
          "accountLabel",
          "address",
          "displayName",
          "groups",
          "subscription"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountLabel": {
            "type": "string",
            "description": "Current label of the owning account."
          },
          "address": {
            "type": "string",
            "description": "Canonical protocol contact address."
          },
          "displayName": {
            "type": "string",
            "description": "Saved provider roster name, or address when no name was supplied."
          },
          "groups": {
            "type": "array",
            "description": "Provider roster categories, not chat-room membership.",
            "items": {
              "type": "string"
            }
          },
          "subscription": {
            "type": "string",
            "description": "Provider-observed presence subscription, without changing it.",
            "enum": [
              "none",
              "to",
              "from",
              "both"
            ]
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          }
        }
      },
      "ContactSource": {
        "type": "object",
        "description": "Last observed roster coverage for one account. A known snapshot remains cached when disconnected.",
        "additionalProperties": false,
        "required": [
          "accountId",
          "state"
        ],
        "properties": {
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "state": {
            "type": "string",
            "description": "Unknown before a successful observation; unavailable after a failed refresh, preserving cached entries.",
            "enum": [
              "unknown",
              "known",
              "unavailable"
            ]
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC time of the last successful complete roster observation."
          }
        }
      },
      "ContactPage": {
        "type": "object",
        "description": "Bounded saved contact results and observation coverage.",
        "additionalProperties": false,
        "required": [
          "items",
          "sources"
        ],
        "example": {
          "items": [],
          "sources": []
        },
        "properties": {
          "items": {
            "type": "array",
            "description": "Matching contacts in stable UUID order.",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/Contact"
            }
          },
          "nextCursor": {
            "type": "string",
            "maxLength": 512,
            "description": "Opaque next matching page, absent at the end."
          },
          "sources": {
            "type": "array",
            "description": "Observation status of accounts in the requested scope.",
            "items": {
              "$ref": "#/components/schemas/ContactSource"
            }
          }
        }
      },
      "MessageSearchHit": {
        "type": "object",
        "description": "A matching current or verified earlier content revision with current canonical navigation context. Multiple hits for one message occur only with explicit edit history.",
        "additionalProperties": false,
        "required": [
          "message",
          "accountLabel",
          "conversationTitle",
          "matchedRevisionId",
          "matchedAt",
          "historical",
          "matchKind",
          "excerpt"
        ],
        "properties": {
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "accountLabel": {
            "type": "string",
            "description": "Current owning account label."
          },
          "conversationTitle": {
            "type": "string",
            "description": "Current source conversation title."
          },
          "conversationAddress": {
            "type": "string",
            "description": "Canonical address of the source conversation."
          },
          "senderAddress": {
            "type": "string",
            "description": "Owning account address for own messages or peer address for received direct messages. Omitted for other senders."
          },
          "senderAvatarHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "SHA-256 of the retained sender avatar PNG in the owning account, when the sender identity and image are available. Use getAvatarImage to retrieve it."
          },
          "preview": {
            "$ref": "#/components/schemas/MessageSearchPreview"
          },
          "matchedRevisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "matchedAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC creation time of the retained matching content revision."
          },
          "historical": {
            "type": "boolean",
            "description": "True only for an earlier content revision. Independent of message.historical, which describes archive provenance. Message always contains current canonical content; use its id for navigation."
          },
          "matchKind": {
            "type": "string",
            "description": "Whether retained text or current associated metadata supplied the match.",
            "enum": [
              "text",
              "metadata"
            ]
          },
          "excerpt": {
            "type": "string",
            "maxLength": 512,
            "description": "Bounded plain text from matching content or metadata. Never HTML; clients must escape it. Earlier content is represented here, rather than by the current message text."
          }
        }
      },
      "MessageSearchPreview": {
        "type": "object",
        "description": "One indexed shared item from the matching current message. Prefer an item matching the query, then cached artwork.",
        "additionalProperties": false,
        "required": [
          "kind"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "link",
              "document"
            ],
            "description": "Category of the shared item."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Source URL of the item represented by this preview."
          },
          "imageHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Digest of a locally cached thumbnail, or the site icon when it is the only artwork. Use getMediaImage to retrieve it."
          },
          "faviconHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Digest of the locally cached site icon for a link. Use getMediaImage to retrieve it."
          }
        }
      },
      "MessageSearchPage": {
        "type": "object",
        "description": "Retained message search results. Message text is immediate; metadata indexing coverage is reported separately.",
        "additionalProperties": false,
        "required": [
          "query",
          "includeEditHistory",
          "items",
          "metadataIndexing"
        ],
        "example": {
          "query": "software livre",
          "includeEditHistory": false,
          "items": [],
          "metadataIndexing": {
            "state": "complete",
            "indexedMessages": 0,
            "totalMessages": 0
          }
        },
        "properties": {
          "query": {
            "type": "string",
            "maxLength": 200,
            "description": "Search query after trimming surrounding whitespace."
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountIds": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "uniqueItems": true,
            "description": "Applied account set in ascending UUID order, present when accountIds was supplied.",
            "items": {
              "$ref": "#/components/schemas/ID"
            }
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "includeEditHistory": {
            "type": "boolean",
            "description": "Applied earlier-content filter."
          },
          "items": {
            "type": "array",
            "maxItems": 100,
            "description": "Current and explicitly requested historical hits in stable chronological keyset order.",
            "items": {
              "$ref": "#/components/schemas/MessageSearchHit"
            }
          },
          "nextCursor": {
            "type": "string",
            "maxLength": 2048,
            "description": "Opaque cursor for the next matching page, absent at the end."
          },
          "metadataIndexing": {
            "$ref": "#/components/schemas/SharedContentIndexing"
          }
        }
      },
      "SharedContentItem": {
        "type": "object",
        "description": "One shared URL reference with its exact retained source message and current owning labels.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "kind",
          "url",
          "fileName",
          "accountLabel",
          "conversationTitle",
          "message"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "kind": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "audio",
              "link",
              "document"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 4096,
            "description": "Original HTTP/HTTPS reference. Never use as an automatic browser media source."
          },
          "fileName": {
            "type": "string",
            "maxLength": 300
          },
          "accountLabel": {
            "type": "string"
          },
          "conversationTitle": {
            "type": "string"
          },
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "preview": {
            "$ref": "#/components/schemas/MessagePreview"
          },
          "thumbnail": {
            "$ref": "#/components/schemas/VideoThumbnail"
          }
        }
      },
      "SharedContentIndexing": {
        "type": "object",
        "description": "Local source-message indexing progress, scoped by account/conversation but independent of tab/search.",
        "additionalProperties": false,
        "required": [
          "state",
          "indexedMessages",
          "totalMessages"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "complete"
            ]
          },
          "indexedMessages": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          },
          "totalMessages": {
            "type": "integer",
            "format": "int64",
            "minimum": 0
          }
        }
      },
      "SharedContentPage": {
        "type": "object",
        "description": "Bounded shared-content page. Image previews and originals retain their independent hashes and states.",
        "additionalProperties": false,
        "required": [
          "items",
          "indexing"
        ],
        "example": {
          "items": [],
          "indexing": {
            "state": "pending",
            "indexedMessages": 0,
            "totalMessages": 100
          }
        },
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/SharedContentItem"
            }
          },
          "nextCursor": {
            "type": "string",
            "maxLength": 512
          },
          "indexing": {
            "$ref": "#/components/schemas/SharedContentIndexing"
          }
        }
      },
      "PreviewPage": {
        "type": "object",
        "description": "At most three preview references for each selected retained message, up to three hundred items.",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 300,
            "items": {
              "$ref": "#/components/schemas/MessagePreview"
            }
          }
        },
        "example": {
          "items": [
            {
              "id": "019a5df0-0000-7000-8000-000000000001",
              "revisionId": "019a5df0-0000-7000-8000-000000000001",
              "accountId": "019a5df0-0000-7000-8000-000000000001",
              "conversationId": "019a5df0-0000-7000-8000-000000000001",
              "messageId": "019a5df0-0000-7000-8000-000000000001",
              "url": "https://example.com/article",
              "kind": "link",
              "state": "ready",
              "downloadState": "not_downloaded",
              "title": "Shared article",
              "siteName": "Example",
              "updatedAt": "2026-11-08T12:00:00Z"
            }
          ]
        }
      },
      "Avatar": {
        "type": "object",
        "additionalProperties": false,
        "description": "Retained profile image projection, scoped to an account and canonical protocol address. Empty imageHash means the provider explicitly returned no image. Provider errors keep the last known image.",
        "required": [
          "id",
          "revisionId",
          "accountId",
          "address",
          "imageHash",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "address": {
            "type": "string",
            "description": "Canonical account or conversation address; callers treat it as an opaque identity key."
          },
          "imageHash": {
            "type": "string",
            "description": "Empty for no image, otherwise SHA-256 of the normalized PNG; use getAvatarImage to retrieve it.",
            "pattern": "^([0-9a-f]{64})?$"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000001",
          "revisionId": "019a5df0-0000-7000-8000-000000000002",
          "accountId": "019a5df0-0000-7000-8000-000000000003",
          "address": "bob@localhost",
          "imageHash": "",
          "updatedAt": "2026-11-07T12:00:00Z"
        }
      },
      "AvatarPage": {
        "type": "object",
        "additionalProperties": false,
        "description": "Account-scoped profile metadata page.",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Avatar"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/ID"
          }
        },
        "example": {
          "items": []
        }
      },
      "HistoryStatus": {
        "type": "object",
        "additionalProperties": false,
        "description": "Last history recovery pass; counts describe this engine's retained records, not total provider history.",
        "required": [
          "accountId",
          "state",
          "retainedMessages",
          "conversations",
          "updatedAt"
        ],
        "properties": {
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "state": {
            "type": "string",
            "enum": [
              "not_started",
              "syncing",
              "complete",
              "partial",
              "unsupported",
              "error"
            ]
          },
          "retainedMessages": {
            "type": "integer",
            "minimum": 0
          },
          "conversations": {
            "type": "integer",
            "minimum": 0
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastErrorCode": {
            "type": "string",
            "enum": [
              "discovery_failed",
              "archive_failed",
              "unstable_archive"
            ]
          }
        },
        "example": {
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "state": "syncing",
          "retainedMessages": 50,
          "conversations": 3,
          "updatedAt": "2026-11-07T12:00:00Z"
        }
      },
      "ConfigureConnection": {
        "type": "object",
        "description": "Desired enabled connection. Repeating unchanged configuration does not create another session. Credentials are kept in private engine-local files and are excluded from reads/events.",
        "additionalProperties": false,
        "required": [
          "password"
        ],
        "properties": {
          "password": {
            "type": "string",
            "description": "Protocol password; never returned.",
            "minLength": 1,
            "maxLength": 4096,
            "writeOnly": true
          },
          "endpoint": {
            "type": "string",
            "description": "Optional TCP host:port override. TLS verifies the account domain. Omit to use XMPP SRV discovery.",
            "maxLength": 255
          }
        },
        "example": {
          "password": "alice-local-only"
        }
      },
      "RoomArchiveMode": {
        "description": "Automatic recovery policy for older room history.",
        "type": "string",
        "enum": [
          "gradual",
          "on_demand"
        ],
        "example": "gradual"
      },
      "SetRoomArchiveMode": {
        "description": "Replacement room recovery preference.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "mode"
        ],
        "properties": {
          "mode": {
            "$ref": "#/components/schemas/RoomArchiveMode"
          }
        }
      },
      "RoomArchive": {
        "description": "Retained recovery progress for one room. Coverage describes archive pages, not isolated search hits.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "conversationId",
          "mode",
          "initialComplete",
          "olderComplete",
          "availability",
          "textSearch"
        ],
        "properties": {
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "mode": {
            "$ref": "#/components/schemas/RoomArchiveMode"
          },
          "initialComplete": {
            "type": "boolean",
            "example": false
          },
          "olderComplete": {
            "type": "boolean",
            "example": false
          },
          "availability": {
            "type": "string",
            "enum": [
              "unknown",
              "supported",
              "unsupported",
              "unavailable"
            ],
            "example": "supported"
          },
          "textSearch": {
            "type": "string",
            "enum": [
              "unknown",
              "supported",
              "unsupported",
              "unavailable"
            ],
            "example": "supported"
          },
          "windowStart": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-08T12:00:00Z"
          },
          "windowEnd": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-08T12:00:00Z"
          },
          "oldestCoveredAt": {
            "description": "Oldest confirmed boundary of continuous recovered history.",
            "type": "string",
            "format": "date-time",
            "example": "2026-11-08T12:00:00Z"
          }
        }
      },
      "RoomArchiveRequest": {
        "description": "One user-initiated room archive action.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "action"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "focus",
              "older",
              "context",
              "search"
            ],
            "example": "focus"
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "query": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "example": "conference"
          },
          "cursor": {
            "description": "Opaque position scoped to this room and search term.",
            "type": "string",
            "maxLength": 8192,
            "example": "opaque-page-position"
          }
        }
      },
      "RoomArchiveResult": {
        "description": "Outcome of the requested archive operation.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "archive",
          "outcome",
          "items"
        ],
        "properties": {
          "archive": {
            "$ref": "#/components/schemas/RoomArchive"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "stored",
              "complete",
              "unsupported",
              "unavailable"
            ],
            "example": "stored"
          },
          "items": {
            "type": "array",
            "maxItems": 50,
            "example": [],
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "nextCursor": {
            "description": "Next bounded search page for the same room and term.",
            "type": "string",
            "maxLength": 8192,
            "example": "opaque-page-position"
          }
        }
      },
      "OpenConversation": {
        "type": "object",
        "description": "Open or find a direct conversation in a registered account.",
        "additionalProperties": false,
        "required": [
          "accountId",
          "address"
        ],
        "properties": {
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "address": {
            "type": "string",
            "description": "Destination protocol address, normalized by the owning adapter.",
            "minLength": 1,
            "maxLength": 1023
          }
        },
        "example": {
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "address": "bob@localhost"
        }
      },
      "UpdateConversationPreferences": {
        "type": "object",
        "description": "Field-isolated archive, favorite or direct message request acceptance with a current conversation revision precondition.",
        "additionalProperties": false,
        "minProperties": 3,
        "required": [
          "accountId",
          "expectedRevisionId"
        ],
        "properties": {
          "accountId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Owning account assertion; a different account is rejected without changing the conversation."
          },
          "expectedRevisionId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Exact current conversation revision. A stale revision yields state_conflict with currentConversation."
          },
          "archived": {
            "type": "boolean",
            "description": "Desired archive state. Incoming messages never automatically unarchive a conversation."
          },
          "favorite": {
            "type": "boolean",
            "description": "Desired conversation favorite state, separate from source-message stars."
          },
          "acceptRequest": {
            "type": "boolean",
            "description": "Set to true to accept a pending direct message request. Use this action on its own."
          }
        },
        "example": {
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "expectedRevisionId": "019a5df0-0000-7000-8000-000000000002",
          "favorite": true
        }
      },
      "Conversation": {
        "type": "object",
        "description": "One account-scoped conversation. Direct conversations support text sending; saved rooms and authorized room archives are recovered read-only in this increment.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "accountId",
          "kind",
          "address",
          "title",
          "lastMessage",
          "updatedAt",
          "unreadCount",
          "archived",
          "favorite"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "kind": {
            "type": "string",
            "description": "Conversation kind.",
            "enum": [
              "direct",
              "room"
            ]
          },
          "address": {
            "type": "string",
            "description": "Canonical peer address."
          },
          "title": {
            "type": "string",
            "description": "Display title; initially the peer address."
          },
          "lastMessage": {
            "type": "string",
            "description": "Original text preview of the most recently retained message, empty while a request is pending."
          },
          "request": {
            "type": "string",
            "description": "Pending user decision. Omitted for ordinary conversations.",
            "enum": [
              "checking",
              "message",
              "room_invitation"
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC timestamp."
          },
          "unreadCount": {
            "type": "integer",
            "minimum": 0
          },
          "unreadMention": {
            "type": "boolean",
            "description": "True when an unread incoming room message mentions the owning account by its room nickname. Omitted means false."
          },
          "archived": {
            "type": "boolean"
          },
          "favorite": {
            "type": "boolean"
          }
        },
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000001",
          "revisionId": "019a5df0-0000-7000-8000-000000000001",
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "kind": "direct",
          "address": "bob@localhost",
          "title": "bob@localhost",
          "lastMessage": "Hello from Oie!",
          "updatedAt": "2026-11-07T12:00:00Z",
          "unreadCount": 0,
          "archived": false,
          "favorite": false
        }
      },
      "DetailState": {
        "type": "string",
        "description": "Observation state; unavailable or missing information never means an empty room or an offline person.",
        "enum": [
          "unknown",
          "pending",
          "known",
          "partial",
          "forbidden",
          "unavailable",
          "not_connected",
          "unsupported"
        ]
      },
      "PresenceObservation": {
        "type": "object",
        "description": "Observed presence from authorized traffic, without active probes or inferred offline state.",
        "additionalProperties": false,
        "required": [
          "state"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "unknown",
              "available",
              "away",
              "busy",
              "extended_away",
              "chat",
              "unavailable"
            ]
          },
          "statusText": {
            "type": "string",
            "maxLength": 300
          },
          "observedAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC time this presence was observed, absent for unknown presence."
          }
        }
      },
      "ConversationParticipant": {
        "type": "object",
        "description": "A scoped peer or room-occupant observation; its identifier is not a verified personal identity.",
        "additionalProperties": false,
        "required": [
          "id",
          "displayName",
          "source",
          "presence"
        ],
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 1024,
            "description": "Opaque identifier scoped to this account and conversation. Never use a nickname as a profile identity."
          },
          "displayName": {
            "type": "string",
            "maxLength": 300
          },
          "source": {
            "type": "string",
            "enum": [
              "discovery",
              "presence"
            ]
          },
          "presence": {
            "$ref": "#/components/schemas/PresenceObservation"
          }
        }
      },
      "ConversationParticipants": {
        "type": "object",
        "description": "Bounded peer or visible-occupant snapshot; no persistent membership roster or completeness guarantee.",
        "additionalProperties": false,
        "required": [
          "scope",
          "state",
          "items",
          "truncated"
        ],
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "peer",
              "occupants"
            ]
          },
          "state": {
            "$ref": "#/components/schemas/DetailState"
          },
          "items": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/ConversationParticipant"
            }
          },
          "truncated": {
            "type": "boolean"
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ConversationDetails": {
        "type": "object",
        "description": "Session-local conversation observations combined with the owning account connection, without changing retained conversation content.",
        "example": {
          "conversationId": "019a5df0-0000-7000-8000-000000000001",
          "accountId": "019a5df0-0000-7000-8000-000000000002",
          "kind": "room",
          "address": "room@conference.example.test",
          "state": "pending",
          "accountConnection": {
            "accountId": "019a5df0-0000-7000-8000-000000000002",
            "state": "connected",
            "updatedAt": "2026-11-08T12:00:00Z"
          },
          "roomSession": "not_joined",
          "features": [],
          "participants": {
            "scope": "occupants",
            "state": "pending",
            "items": [],
            "truncated": false
          }
        },
        "additionalProperties": false,
        "required": [
          "conversationId",
          "accountId",
          "kind",
          "address",
          "state",
          "accountConnection",
          "features",
          "participants"
        ],
        "properties": {
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "kind": {
            "type": "string",
            "enum": [
              "direct",
              "room"
            ],
            "example": "room"
          },
          "address": {
            "type": "string"
          },
          "state": {
            "$ref": "#/components/schemas/DetailState"
          },
          "accountConnection": {
            "$ref": "#/components/schemas/Connection"
          },
          "checkedAt": {
            "type": "string",
            "format": "date-time"
          },
          "name": {
            "type": "string",
            "maxLength": 300
          },
          "description": {
            "type": "string",
            "maxLength": 2000
          },
          "topic": {
            "type": "string",
            "maxLength": 2000
          },
          "occupantCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647,
            "description": "Server-reported occupancy, independent of visibility or returned list length."
          },
          "roomSession": {
            "type": "string",
            "enum": [
              "not_joined",
              "joining",
              "joined",
              "failed",
              "unknown"
            ]
          },
          "features": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "type": "string",
              "enum": [
                "public",
                "hidden",
                "members_only",
                "open",
                "persistent",
                "temporary",
                "moderated",
                "unmoderated",
                "password_protected",
                "unsecured",
                "anonymous",
                "non_anonymous"
              ]
            }
          },
          "participants": {
            "$ref": "#/components/schemas/ConversationParticipants"
          },
          "peerPresence": {
            "$ref": "#/components/schemas/PresenceObservation"
          }
        }
      },
      "ConversationPage": {
        "type": "object",
        "description": "Conversations in descending engine activity order. New activity can move items between pages; reconcile using stable IDs and SSE.",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Conversation"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque engine-scoped activity cursor."
          }
        },
        "example": {
          "items": []
        }
      },
      "NullableReactionRevision": {
        "type": "string",
        "format": "uuid",
        "x-go-type": "string",
        "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
        "nullable": true,
        "description": "Observed reaction revision UUID, or null before the first set.",
        "example": "019a5df0-0000-7000-8000-000000000004"
      },
      "ReactionEmojiSet": {
        "type": "array",
        "description": "Complete desired set of at most eight distinct single Unicode emoji graphemes. Empty clears the set.",
        "maxItems": 8,
        "uniqueItems": true,
        "items": {
          "type": "string",
          "minLength": 1,
          "maxLength": 32,
          "example": "👍"
        },
        "example": [
          "👍"
        ]
      },
      "ReactionItem": {
        "type": "object",
        "description": "One emoji and its count across distinct verified actors.",
        "additionalProperties": false,
        "required": [
          "emoji",
          "count",
          "own"
        ],
        "properties": {
          "emoji": {
            "type": "string",
            "example": "👍"
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "example": 2
          },
          "own": {
            "type": "boolean",
            "example": true
          }
        }
      },
      "ReactionActorEmojiSet": {
        "type": "array",
        "description": "A verified actor's latest nonempty set of distinct emoji graphemes.",
        "minItems": 1,
        "maxItems": 8,
        "uniqueItems": true,
        "items": {
          "type": "string",
          "minLength": 1,
          "maxLength": 32,
          "example": "👍"
        },
        "example": [
          "👍"
        ]
      },
      "ReactionActor": {
        "type": "object",
        "additionalProperties": false,
        "description": "One verified actor's latest nonempty set, with a retained display name and optional cached avatar. Identity is scoped to the snapshot account and conversation. Reading never discovers or links identities.",
        "required": [
          "address",
          "displayName",
          "own",
          "emojis"
        ],
        "properties": {
          "address": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1023,
            "description": "Protocol-verified actor address or an opaque occupant identifier prefixed with occupant: in a semianonymous room.",
            "example": "alice@example.test"
          },
          "displayName": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1023,
            "description": "Observed room nickname for opaque occupants. For other actors, the active contact name, observed name or address.",
            "example": "Alice"
          },
          "avatarHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Cached avatar PNG SHA-256 for a verified address in the same account. Omitted for opaque occupants.",
            "example": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
          },
          "own": {
            "type": "boolean",
            "example": true
          },
          "emojis": {
            "$ref": "#/components/schemas/ReactionActorEmojiSet"
          }
        }
      },
      "MessageReactions": {
        "type": "object",
        "example": {
          "messageId": "019a5df0-0000-7000-8000-000000000003",
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "conversationId": "019a5df0-0000-7000-8000-000000000002",
          "revisionId": null,
          "ownRevisionId": null,
          "ownEmojis": [],
          "items": [],
          "eligible": true,
          "maxEmojis": 8
        },
        "description": "Canonical message reaction totals, own set and scoped mutation availability.",
        "additionalProperties": false,
        "required": [
          "messageId",
          "accountId",
          "conversationId",
          "revisionId",
          "ownRevisionId",
          "ownEmojis",
          "items",
          "eligible",
          "maxEmojis"
        ],
        "properties": {
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/NullableReactionRevision"
          },
          "ownRevisionId": {
            "$ref": "#/components/schemas/NullableReactionRevision"
          },
          "ownEmojis": {
            "$ref": "#/components/schemas/ReactionEmojiSet"
          },
          "items": {
            "type": "array",
            "example": [],
            "items": {
              "$ref": "#/components/schemas/ReactionItem"
            }
          },
          "actors": {
            "type": "array",
            "description": "Complete retained actor sets matching the emoji totals. Optional for compatibility with engines before API 0.27; current engines always include it, including an empty array. Empty sets and conflicting observations are omitted.",
            "example": [],
            "items": {
              "$ref": "#/components/schemas/ReactionActor"
            }
          },
          "eligible": {
            "type": "boolean",
            "example": true
          },
          "reason": {
            "type": "string",
            "enum": [
              "target_unavailable",
              "session_changed",
              "reaction_pending",
              "protocol_unavailable"
            ],
            "example": "session_changed"
          },
          "maxEmojis": {
            "type": "integer",
            "minimum": 1,
            "maximum": 8,
            "description": "Effective app and currently observed protocol limit.",
            "example": 8
          },
          "allowedEmojis": {
            "type": "array",
            "maxItems": 256,
            "description": "Optional advertised allowed choices, independent of the smaller per-message selection limit.",
            "example": [
              "👍",
              "❤️"
            ],
            "items": {
              "type": "string",
              "maxLength": 32,
              "example": "👍"
            }
          },
          "pendingCommand": {
            "$ref": "#/components/schemas/MessageReactionCommand"
          }
        }
      },
      "MessageReactionsList": {
        "type": "object",
        "example": {
          "items": []
        },
        "description": "Reaction snapshots for a bounded retained conversation page.",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/MessageReactions"
            }
          }
        }
      },
      "SetMessageReactions": {
        "type": "object",
        "example": {
          "expectedOwnRevisionId": null,
          "emojis": [
            "👍"
          ]
        },
        "description": "Complete desired own reaction set guarded by the observed own revision.",
        "additionalProperties": false,
        "required": [
          "expectedOwnRevisionId",
          "emojis"
        ],
        "properties": {
          "expectedOwnRevisionId": {
            "$ref": "#/components/schemas/NullableReactionRevision"
          },
          "emojis": {
            "$ref": "#/components/schemas/ReactionEmojiSet"
          }
        }
      },
      "MessageReactionCommand": {
        "type": "object",
        "description": "Durable immutable reaction intent and current transmission evidence.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "messageId",
          "accountId",
          "conversationId",
          "expectedOwnRevisionId",
          "emojis",
          "state",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "expectedOwnRevisionId": {
            "$ref": "#/components/schemas/NullableReactionRevision"
          },
          "emojis": {
            "$ref": "#/components/schemas/ReactionEmojiSet"
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "dispatching",
              "submitted",
              "unknown",
              "failed"
            ],
            "example": "queued"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-09T12:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-09T12:00:00Z"
          },
          "lastErrorCode": {
            "type": "string",
            "maxLength": 100,
            "example": "session_changed"
          }
        }
      },
      "MessageEditTarget": {
        "type": "object",
        "description": "Current canonical text and advisory eligibility for a distinct sent-message correction.",
        "additionalProperties": false,
        "required": [
          "messageId",
          "accountId",
          "conversationId",
          "currentRevisionId",
          "originalText",
          "eligible"
        ],
        "example": {
          "messageId": "019a5df0-0000-7000-8000-000000000003",
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "conversationId": "019a5df0-0000-7000-8000-000000000002",
          "currentRevisionId": "019a5df0-0000-7000-8000-000000000004",
          "originalText": "Text to correct",
          "eligible": true
        },
        "properties": {
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "currentRevisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "originalText": {
            "type": "string",
            "description": "Exact current source text. For an eligible target this is plain text without reply or mention payloads.",
            "example": "Text to correct"
          },
          "eligible": {
            "type": "boolean",
            "example": true
          },
          "reason": {
            "type": "string",
            "description": "Stable reason when the target is not currently eligible.",
            "enum": [
              "not_own",
              "not_latest",
              "not_submitted",
              "foreign_source",
              "unsupported_payload",
              "session_changed",
              "edit_pending",
              "protocol_unavailable",
              "history_unavailable"
            ],
            "example": "not_latest"
          }
        }
      },
      "EditMessage": {
        "type": "object",
        "description": "Immutable correction intent; changing either field requires a new operation after a definitive outcome.",
        "additionalProperties": false,
        "required": [
          "expectedRevisionId",
          "originalText"
        ],
        "example": {
          "expectedRevisionId": "019a5df0-0000-7000-8000-000000000004",
          "originalText": "Corrected text"
        },
        "properties": {
          "expectedRevisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "originalText": {
            "type": "string",
            "minLength": 1,
            "maxLength": 16384,
            "example": "Corrected text"
          }
        }
      },
      "MessageEditCommand": {
        "type": "object",
        "description": "Durable correction intent and its actual transmission state, separate from the original message send.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "messageId",
          "accountId",
          "conversationId",
          "expectedRevisionId",
          "originalText",
          "state",
          "createdAt",
          "updatedAt"
        ],
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000005",
          "revisionId": "019a5df0-0000-7000-8000-000000000006",
          "messageId": "019a5df0-0000-7000-8000-000000000003",
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "conversationId": "019a5df0-0000-7000-8000-000000000002",
          "expectedRevisionId": "019a5df0-0000-7000-8000-000000000004",
          "originalText": "Corrected text",
          "state": "queued",
          "createdAt": "2026-11-09T12:00:00Z",
          "updatedAt": "2026-11-09T12:00:00Z"
        },
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "expectedRevisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "originalText": {
            "type": "string",
            "minLength": 1,
            "maxLength": 16384,
            "example": "Corrected text"
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "dispatching",
              "submitted",
              "unknown",
              "failed"
            ],
            "example": "queued"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-09T12:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "example": "2026-11-09T12:00:00Z"
          },
          "lastErrorCode": {
            "type": "string",
            "maxLength": 100,
            "description": "Stable failure or uncertainty code, never a raw provider error.",
            "example": "state_conflict"
          }
        }
      },
      "MessageEditHistoryItem": {
        "type": "object",
        "description": "One immutable observed content version, not a transport-state transition.",
        "additionalProperties": false,
        "required": [
          "revisionId",
          "sourceMessageId",
          "createdAt",
          "originalText",
          "sender",
          "isCurrent",
          "isOriginal",
          "parentRevisionIds"
        ],
        "properties": {
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "sourceMessageId": {
            "$ref": "#/components/schemas/ID"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "Observed authored UTC instant; ordering does not establish causality.",
            "example": "2026-11-09T12:00:00Z"
          },
          "originalText": {
            "type": "string",
            "description": "Exact retained source text including authored Markdown and compatibility fallback.",
            "example": "Corrected text"
          },
          "sender": {
            "$ref": "#/components/schemas/Sender"
          },
          "isCurrent": {
            "type": "boolean",
            "description": "This is the selected content head, independently of timestamp order.",
            "example": true
          },
          "isOriginal": {
            "type": "boolean",
            "description": "Verified original or ordinary initial source; false for an orphan correction whose earlier source was never observed.",
            "example": false
          },
          "parentRevisionIds": {
            "type": "array",
            "maxItems": 128,
            "description": "Verified observed content ancestry; omitted ancestors are not inferred.",
            "items": {
              "$ref": "#/components/schemas/ID"
            },
            "example": []
          }
        }
      },
      "MessageEditHistoryPage": {
        "type": "object",
        "description": "A bounded page from an exact snapshot of one canonical message's observed content history.",
        "additionalProperties": false,
        "required": [
          "requestedMessageId",
          "messageId",
          "accountId",
          "conversationId",
          "currentRevisionId",
          "snapshot",
          "items"
        ],
        "properties": {
          "requestedMessageId": {
            "$ref": "#/components/schemas/ID"
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "currentRevisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "snapshot": {
            "type": "string",
            "minLength": 64,
            "maxLength": 64,
            "pattern": "^[a-f0-9]{64}$",
            "description": "Digest of scoped content history and selected head; delivery-state changes do not invalidate it.",
            "example": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
          },
          "items": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/MessageEditHistoryItem"
            },
            "example": []
          },
          "nextCursor": {
            "type": "string",
            "maxLength": 2048,
            "description": "Present only when older observed versions remain in the same snapshot.",
            "example": "opaque-history-cursor"
          }
        }
      },
      "MessageSenderProfile": {
        "type": "object",
        "description": "Published profile data for a retained source author. Optional values never imply a verified legal name.",
        "additionalProperties": false,
        "required": [
          "accountId",
          "conversationId",
          "messageId",
          "state"
        ],
        "properties": {
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "state": {
            "type": "string",
            "enum": [
              "known",
              "unavailable",
              "not_connected",
              "unsupported",
              "not_disclosed"
            ],
            "example": "not_disclosed"
          },
          "personalAddress": {
            "type": "string",
            "maxLength": 1024,
            "description": "Personal user@host disclosed by authenticated protocol evidence for this message's author, never guessed from its nickname or vCard.",
            "example": "juliet@example.test"
          },
          "fullName": {
            "type": "string",
            "maxLength": 300,
            "description": "Published full name from the known author's profile; plain text, not a verified legal name.",
            "example": "Juliet Example"
          }
        }
      },
      "Sender": {
        "type": "object",
        "description": "Engine-local source identity. For recovered room messages, this is the room-scoped source with an observed speaker nickname, not a verified personal identity.",
        "additionalProperties": false,
        "required": [
          "id",
          "displayName"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "displayName": {
            "type": "string",
            "description": "Display name, initially a protocol address."
          }
        },
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000001",
          "displayName": "bob@localhost"
        }
      },
      "MessageReply": {
        "type": "object",
        "description": "A protocol-identified reply projection. It never infers identity from quoted text. Available means exactly one retained target in the same account and conversation matches the validated protocol reference. Unavailable references may retain a bounded compatibility excerpt, but cannot be navigated.",
        "additionalProperties": false,
        "required": [
          "available",
          "bodyText",
          "excerpt"
        ],
        "properties": {
          "available": {
            "type": "boolean",
            "example": true,
            "description": "Whether a unique retained original is available for navigation."
          },
          "messageId": {
            "$ref": "#/components/schemas/ID"
          },
          "senderName": {
            "type": "string",
            "maxLength": 300,
            "description": "Observed original sender display name, not proof of a real-world identity.",
            "example": "Bob"
          },
          "excerpt": {
            "type": "string",
            "maxLength": 512,
            "description": "Plain bounded original body or protocol-marked fallback excerpt. Never provider HTML.",
            "example": "We should bake a cake."
          },
          "bodyText": {
            "type": "string",
            "description": "Display body after valid protocol-marked Unicode fallback ranges are moved to the quote card. originalText remains unchanged for copying. Malformed or unrecognized fallback ranges remain in the body.",
            "example": "Great idea!"
          }
        },
        "example": {
          "available": true,
          "messageId": "019a5df0-0000-7000-8000-000000000001",
          "senderName": "Bob",
          "excerpt": "We should bake a cake.",
          "bodyText": "Great idea!"
        }
      },
      "MessageContext": {
        "type": "object",
        "description": "Bounded retained timeline window. Neighbor availability is relative to the returned extremes. Validated correction-source aliases resolve to the original logical message UUID returned as targetId. Page outward through listMessages using the oldest item UUID with direction older and the newest item UUID with direction newer. Neighbor flags describe availability beyond these returned extremes.",
        "additionalProperties": false,
        "required": [
          "targetId",
          "items",
          "hasOlder",
          "hasNewer"
        ],
        "properties": {
          "targetId": {
            "$ref": "#/components/schemas/ID"
          },
          "requestedTargetId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Present only when the requested retained correction-source UUID resolves to a different canonical targetId. This is an engine-validated alias within this conversation, not a client-supplied replacement target."
          },
          "items": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "hasOlder": {
            "type": "boolean",
            "example": false
          },
          "hasNewer": {
            "type": "boolean",
            "example": true
          }
        },
        "example": {
          "targetId": "019a5df0-0000-7000-8000-000000000001",
          "items": [
            {
              "id": "019a5df0-0000-7000-8000-000000000001",
              "revisionId": "019a5df0-0000-7000-8000-000000000001",
              "accountId": "019a5df0-0000-7000-8000-000000000001",
              "conversationId": "019a5df0-0000-7000-8000-000000000001",
              "sender": {
                "id": "019a5df0-0000-7000-8000-000000000001",
                "displayName": "bob@localhost"
              },
              "originalText": "Hello from Oie!",
              "createdAt": "2026-11-07T12:00:00Z",
              "isOwn": false,
              "sendState": "received"
            }
          ],
          "hasOlder": false,
          "hasNewer": true
        }
      },
      "Message": {
        "type": "object",
        "description": "Retained original text and actual send evidence. submitted means the stanza was written to the authenticated stream; it does not prove provider acceptance, recipient delivery, or read state. unknown is not automatically resent.",
        "additionalProperties": false,
        "required": [
          "id",
          "revisionId",
          "accountId",
          "conversationId",
          "sender",
          "originalText",
          "createdAt",
          "isOwn",
          "sendState"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "revisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "sender": {
            "$ref": "#/components/schemas/Sender"
          },
          "mentions": {
            "type": "array",
            "maxItems": 32,
            "description": "Frozen selected mentions with Unicode ranges in authored text, excluding any reply compatibility prefix.",
            "items": {
              "$ref": "#/components/schemas/MessageMention"
            }
          },
          "mentionsYou": {
            "type": "boolean",
            "description": "True when the current received room message text mentions the owning account by its observed room nickname. Omitted means false."
          },
          "reply": {
            "$ref": "#/components/schemas/MessageReply"
          },
          "replyToMessageId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Canonical target UUID frozen in an outgoing reply intent, including a text-only fallback. It does not imply the original is still available. Reply bodyText preserves the authored response for receipt reconciliation."
          },
          "expectedReplyRevisionId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Source revision explicitly required when the reply was scheduled or the current quote was replaced. Omitted when the caller supplied no review precondition for the current quote."
          },
          "expectedReplyExcerptSha256": {
            "$ref": "#/components/schemas/ReplyExcerptSHA256"
          },
          "originalText": {
            "type": "string",
            "description": "Exact text of the current retained content revision. Validated incoming corrections retain the logical message UUID and original creation time; prior source bodies remain retained. Copying returns this text without removing formatting or compatibility quoting."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC timestamp."
          },
          "timelineAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC position of a released scheduled message in the conversation. Use createdAt when omitted. The original createdAt is preserved."
          },
          "isOwn": {
            "type": "boolean"
          },
          "historical": {
            "type": "boolean",
            "description": "True for archive imports. Omitted means false. Clients must not treat these as new unread messages or fresh notifications."
          },
          "edited": {
            "type": "boolean",
            "description": "True when a validated protocol correction supplies the current content. Omitted means false. Send-state revisions alone do not mark a message edited.",
            "example": true
          },
          "editedAt": {
            "type": "string",
            "format": "date-time",
            "description": "UTC source timestamp of the selected protocol correction, when edited is true. Receiving or importing an old correction does not give it a new edit timestamp."
          },
          "sendState": {
            "type": "string",
            "description": "Durable transport evidence.",
            "enum": [
              "received",
              "queued",
              "dispatching",
              "submitted",
              "unknown",
              "failed",
              "cancelled"
            ]
          },
          "scheduledAt": {
            "type": "string",
            "format": "date-time",
            "description": "Configured earliest dispatch instant in UTC, when retained. A scheduled send remains queued while waiting for that instant or for connectivity. Send now clears this field. createdAt is the original acceptance time.",
            "example": "2026-11-12T15:30:00Z"
          },
          "dispatchStarted": {
            "type": "boolean",
            "description": "True once this intent has crossed its first dispatch claim. Queued retries with this flag cannot be edited, rescheduled, cancelled or sent now.",
            "example": true
          },
          "composition": {
            "allOf": [
              {
                "$ref": "#/components/schemas/OutgoingComposition"
              }
            ],
            "description": "Accepted authored input for an editable own pending message and its acceptance or mutation snapshots. Reopen the editor from this input. Reply rendering and attachment metadata are exposed on the same Message."
          },
          "attachments": {
            "type": "array",
            "minItems": 1,
            "description": "Ordered metadata for retained outgoing attachments.",
            "items": {
              "$ref": "#/components/schemas/StagedAttachment"
            }
          }
        },
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000001",
          "revisionId": "019a5df0-0000-7000-8000-000000000001",
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "conversationId": "019a5df0-0000-7000-8000-000000000001",
          "sender": {
            "id": "019a5df0-0000-7000-8000-000000000001",
            "displayName": "bob@localhost"
          },
          "originalText": "Hello from Oie!",
          "createdAt": "2026-11-07T12:00:00Z",
          "isOwn": false,
          "sendState": "received"
        }
      },
      "MessagePage": {
        "type": "object",
        "description": "Visible logical messages newest first by timelineAt or createdAt at UTC millisecond precision and UUID tie-break. Unreleased schedules and scheduled sends cancelled before dispatch are excluded. Validated correction sources are retained as revisions rather than additional timeline items. Use nextCursor for records in the requested direction; render each page in reverse for a chronological timeline. For newer pages, nextCursor is the newest returned UUID, while older pages use the oldest returned UUID.",
        "additionalProperties": false,
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "nextCursor": {
            "$ref": "#/components/schemas/ID"
          }
        },
        "example": {
          "items": []
        }
      },
      "RoomPreview": {
        "description": "Published room information available to the selected XMPP account before entry.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "address",
          "state"
        ],
        "properties": {
          "address": {
            "type": "string",
            "description": "Canonical bare room address."
          },
          "state": {
            "type": "string",
            "enum": [
              "known",
              "not_found",
              "not_connected",
              "unavailable"
            ]
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "topic": {
            "type": "string"
          },
          "occupantCount": {
            "type": "integer",
            "minimum": 0
          },
          "avatarHash": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "Content hash for a validated PNG thumbnail available through the avatar resource."
          }
        },
        "example": {
          "address": "garden@rooms.example.org",
          "state": "known",
          "name": "Garden"
        }
      },
      "OpenRoom": {
        "description": "Explicit request to enter an existing room or create a new room at the given address.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "address",
          "action"
        ],
        "properties": {
          "address": {
            "type": "string",
            "minLength": 3,
            "maxLength": 2047,
            "description": "Bare XMPP room address."
          },
          "action": {
            "type": "string",
            "enum": [
              "join",
              "create"
            ]
          },
          "nickname": {
            "type": "string",
            "maxLength": 100,
            "description": "Optional room nickname. Defaults to the account nickname."
          },
          "name": {
            "type": "string",
            "maxLength": 100,
            "description": "Optional published name for a newly created room."
          }
        },
        "example": {
          "address": "garden@rooms.example.org",
          "action": "join"
        }
      },
      "RoomSession": {
        "type": "object",
        "additionalProperties": false,
        "description": "Own engine's room intent and observed session, distinct from account connectivity and public occupant discovery.",
        "required": [
          "accountId",
          "conversationId",
          "active",
          "state",
          "updatedAt"
        ],
        "properties": {
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "intentId": {
            "$ref": "#/components/schemas/ID"
          },
          "intentRevisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "active": {
            "type": "boolean",
            "description": "This engine has a durable active join intent. False after explicit leave or revoked access."
          },
          "state": {
            "type": "string",
            "enum": [
              "not_joined",
              "joining",
              "joined",
              "failed"
            ]
          },
          "nickname": {
            "type": "string",
            "maxLength": 100,
            "description": "Selected room nickname when known."
          },
          "lastErrorCode": {
            "type": "string",
            "enum": [
              "not_connected",
              "forbidden",
              "not_authorized",
              "nickname_conflict",
              "registration_required",
              "room_unavailable",
              "removed",
              "timeout",
              "unsupported",
              "unknown"
            ]
          },
          "creationError": {
            "type": "string",
            "enum": [
              "room_exists",
              "creation_failed"
            ],
            "description": "More specific reason when the requested creation could not be confirmed."
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "example": {
          "accountId": "019a5df0-0000-7000-8000-000000000001",
          "conversationId": "019a5df0-0000-7000-8000-000000000002",
          "active": false,
          "state": "not_joined",
          "updatedAt": "2026-11-08T12:00:00Z"
        }
      },
      "JoinRoom": {
        "description": "Explicit room-entry command with optimistic intent revision.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "expectedRevisionId"
        ],
        "properties": {
          "expectedConversationRevisionId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Current conversation revision required when accepting a room invitation."
          },
          "nickname": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "description": "Optional nickname; the adapter chooses its account default when omitted. No password is accepted in this increment."
          },
          "expectedRevisionId": {
            "$ref": "#/components/schemas/NullableRevisionID"
          }
        },
        "example": {
          "expectedRevisionId": null
        }
      },
      "LeaveRoom": {
        "description": "Explicit room-exit command preserving retained history and pending work.",
        "type": "object",
        "additionalProperties": false,
        "required": [
          "expectedRevisionId"
        ],
        "properties": {
          "expectedRevisionId": {
            "$ref": "#/components/schemas/NullableRevisionID"
          }
        },
        "example": {
          "expectedRevisionId": "019a5df0-0000-7000-8000-000000000003"
        }
      },
      "MentionSelection": {
        "type": "object",
        "description": "A user-selected, account-and-room-scoped participant with an authored Unicode text span.",
        "additionalProperties": false,
        "required": [
          "participantId",
          "begin",
          "end"
        ],
        "properties": {
          "participantId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1024,
            "description": "Opaque candidate identifier scoped to the sending account and room."
          },
          "begin": {
            "type": "integer",
            "minimum": 0,
            "maximum": 16384,
            "description": "Inclusive Unicode code-point offset in authored originalText, before a reply compatibility prefix."
          },
          "end": {
            "type": "integer",
            "minimum": 1,
            "maximum": 16384,
            "description": "Exclusive Unicode code-point offset. The span must match the candidate's nickname at word boundaries or its original at-prefixed insertText. Composer-added punctuation and spacing are outside the span."
          }
        }
      },
      "MessageMention": {
        "type": "object",
        "description": "Accepted mention selection with frozen text or structured delivery mode; protocol targets remain private to the engine.",
        "additionalProperties": false,
        "required": [
          "participantId",
          "begin",
          "end",
          "referenceMode"
        ],
        "properties": {
          "participantId": {
            "type": "string",
            "maxLength": 1024
          },
          "begin": {
            "type": "integer",
            "minimum": 0
          },
          "end": {
            "type": "integer",
            "minimum": 1
          },
          "referenceMode": {
            "type": "string",
            "enum": [
              "text",
              "structured"
            ],
            "description": "Frozen delivery mode; structured adds adapter-validated protocol metadata while retaining the text."
          }
        }
      },
      "MentionCandidate": {
        "type": "object",
        "description": "A currently observed room occupant with readable insertion text and effective reference mode.",
        "additionalProperties": false,
        "required": [
          "id",
          "displayName",
          "insertText",
          "referenceMode"
        ],
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 1024,
            "description": "Opaque account-and-room-scoped identifier, never a verified personal identity."
          },
          "displayName": {
            "type": "string",
            "maxLength": 300
          },
          "insertText": {
            "type": "string",
            "maxLength": 1100,
            "description": "Room nickname prefixed by one at sign. Clients can use the nickname without that prefix and add punctuation outside the selected span."
          },
          "referenceMode": {
            "type": "string",
            "enum": [
              "text",
              "structured"
            ]
          }
        }
      },
      "MentionCandidates": {
        "type": "object",
        "description": "Bounded visible-occupant choices without personal protocol identifiers or a completeness claim.",
        "additionalProperties": false,
        "required": [
          "conversationId",
          "accountId",
          "state",
          "items",
          "truncated"
        ],
        "properties": {
          "bareNicknameSpans": {
            "type": "boolean",
            "description": "The engine accepts a selected span containing just the nickname. Omission means only the original at-prefixed span is supported.",
            "example": true
          },
          "conversationId": {
            "$ref": "#/components/schemas/ID"
          },
          "accountId": {
            "$ref": "#/components/schemas/ID"
          },
          "state": {
            "type": "string",
            "enum": [
              "known",
              "partial",
              "unavailable",
              "not_connected"
            ],
            "example": "known"
          },
          "items": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/MentionCandidate"
            },
            "example": [
              {
                "id": "room@conference.example.org/Juliet",
                "displayName": "Juliet",
                "insertText": "@Juliet",
                "referenceMode": "text"
              }
            ]
          },
          "truncated": {
            "type": "boolean",
            "example": false
          }
        }
      },
      "SendMessage": {
        "type": "object",
        "description": "Authored text and optional reply, mentions and ordered staged attachments. Links are part of the authored text. Reply targets use canonical UUIDs scoped to the destination. The engine freezes reply and mention references before enqueueing. The resulting Message.originalText follows the existing retained body format, including any reply compatibility text. Message.composition contains the editable input for pending sends.",
        "additionalProperties": false,
        "required": [
          "originalText"
        ],
        "properties": {
          "originalText": {
            "type": "string",
            "description": "Exact authored UTF-8 text. Empty or whitespace-only text requires at least one attachment.",
            "minLength": 0,
            "maxLength": 16384
          },
          "mentions": {
            "type": "array",
            "maxItems": 32,
            "description": "Selected room mentions in authored text. Sorted, nonoverlapping spans are part of the immutable send intent and idempotency fingerprint. The engine freezes references before enqueueing; clients cannot submit protocol URIs.",
            "items": {
              "$ref": "#/components/schemas/MentionSelection"
            }
          },
          "replyToMessageId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Retained source UUID in this conversation. Omitted for an ordinary message. Part of the immutable send intent and operation fingerprint."
          },
          "expectedReplyRevisionId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Reviewed revision of the retained reply source. Requires replyToMessageId and scheduledAt. A changed or unavailable source rejects the new scheduled send before acceptance."
          },
          "expectedReplyExcerptSha256": {
            "$ref": "#/components/schemas/ReplyExcerptSHA256"
          },
          "attachmentIds": {
            "$ref": "#/components/schemas/StagedAttachmentIDs"
          },
          "scheduledAt": {
            "$ref": "#/components/schemas/ScheduledSendInstant"
          }
        },
        "example": {
          "originalText": "Hello from Oie!"
        },
        "not": {
          "type": "object",
          "properties": {
            "originalText": {
              "type": "string",
              "maxLength": 0
            }
          },
          "not": {
            "type": "object",
            "required": [
              "attachmentIds"
            ]
          }
        }
      },
      "ReplyExcerptSHA256": {
        "type": "string",
        "pattern": "^[0-9a-f]{64}$",
        "description": "SHA-256 of the exact UTF-8 quote excerpt, at most the first 512 Unicode code points of the displayed source body. Optional on reviewed reply mutations and requires expectedReplyRevisionId. The engine preserves it in the original acceptance when supplied.",
        "example": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
      },
      "CancelMessage": {
        "type": "object",
        "description": "Cancel a queued, known-unsent message using its current revision. A state conflict returns currentMessage.",
        "additionalProperties": false,
        "required": [
          "expectedRevisionId"
        ],
        "properties": {
          "expectedRevisionId": {
            "$ref": "#/components/schemas/ID"
          }
        },
        "example": {
          "expectedRevisionId": "019a5df0-0000-7000-8000-000000000001"
        }
      },
      "ScheduledSendInstant": {
        "type": "string",
        "format": "date-time",
        "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-5][0-9](\\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})$",
        "description": "An RFC 3339 instant strictly in the future when the owning engine first accepts it. Supply an explicit offset or Z, with up to nine fractional second digits. The engine normalizes to UTC. Equivalent offsets identify the same instant for idempotency.",
        "example": "2026-11-12T12:30:00-03:00"
      },
      "StagedAttachmentIDs": {
        "type": "array",
        "minItems": 1,
        "maxItems": 16,
        "uniqueItems": true,
        "description": "Ordered unique IDs of available staged files in the same destination scope.",
        "items": {
          "$ref": "#/components/schemas/ID"
        }
      },
      "StagedAttachment": {
        "type": "object",
        "description": "Immutable metadata for locally staged attachment bytes.",
        "additionalProperties": false,
        "required": [
          "id",
          "fileName",
          "mediaType",
          "size",
          "sha256"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/ID"
          },
          "fileName": {
            "type": "string",
            "minLength": 1,
            "description": "Display filename with no storage path.",
            "example": "notes.txt"
          },
          "mediaType": {
            "type": "string",
            "description": "Effective content type for presentation.",
            "example": "text/plain"
          },
          "size": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Exact number of staged bytes.",
            "example": 5
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "SHA-256 computed over the complete staged bytes.",
            "example": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
          }
        },
        "example": {
          "id": "019a5df0-0000-7000-8000-000000000006",
          "fileName": "notes.txt",
          "mediaType": "text/plain",
          "size": 5,
          "sha256": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
        }
      },
      "OutgoingComposition": {
        "type": "object",
        "description": "Complete authored content in the same form used at initial acceptance. Omit an optional field to clear it when replacing the composition.",
        "additionalProperties": false,
        "required": [
          "originalText"
        ],
        "not": {
          "type": "object",
          "properties": {
            "originalText": {
              "type": "string",
              "maxLength": 0
            }
          },
          "not": {
            "type": "object",
            "required": [
              "attachmentIds"
            ]
          }
        },
        "properties": {
          "originalText": {
            "type": "string",
            "maxLength": 16384,
            "description": "Exact UTF-8 text, including links. Empty or whitespace-only text requires at least one attachment."
          },
          "replyToMessageId": {
            "$ref": "#/components/schemas/ID"
          },
          "mentions": {
            "type": "array",
            "maxItems": 32,
            "items": {
              "$ref": "#/components/schemas/MentionSelection"
            }
          },
          "attachmentIds": {
            "$ref": "#/components/schemas/StagedAttachmentIDs"
          }
        },
        "example": {
          "originalText": "Studio entrance: https://example.org/studio",
          "attachmentIds": [
            "019a5df0-0000-7000-8000-000000000006"
          ]
        }
      },
      "UpdatePendingMessage": {
        "type": "object",
        "description": "Supply composition, scheduledAt or both. Each omitted top-level field retains its current value. Null is invalid. Replacing the composition creates a pending revision without a sent-message correction. The frozen reply is preserved when the target is unchanged and no reply precondition is supplied. Use sendScheduledMessageNow to make an existing schedule eligible now.",
        "additionalProperties": false,
        "minProperties": 2,
        "required": [
          "expectedRevisionId"
        ],
        "properties": {
          "expectedRevisionId": {
            "$ref": "#/components/schemas/ID"
          },
          "composition": {
            "$ref": "#/components/schemas/OutgoingComposition"
          },
          "expectedReplyRevisionId": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ID"
              }
            ],
            "description": "Requires a replacement composition with a reply target. Freezes the selected source at this reviewed revision, including when the target UUID is unchanged. A changed or unavailable source rejects the edit with state_conflict and replySourceChanged set to true."
          },
          "expectedReplyExcerptSha256": {
            "$ref": "#/components/schemas/ReplyExcerptSHA256"
          },
          "scheduledAt": {
            "$ref": "#/components/schemas/ScheduledSendInstant"
          }
        },
        "example": {
          "expectedRevisionId": "019a5df0-0000-7000-8000-000000000002",
          "composition": {
            "originalText": "",
            "attachmentIds": [
              "019a5df0-0000-7000-8000-000000000006"
            ]
          },
          "scheduledAt": "2026-11-12T12:30:00-03:00"
        }
      },
      "SendScheduledMessageNow": {
        "type": "object",
        "description": "Revision precondition for making a scheduled message eligible now.",
        "additionalProperties": false,
        "required": [
          "expectedRevisionId"
        ],
        "properties": {
          "expectedRevisionId": {
            "$ref": "#/components/schemas/ID"
          }
        }
      },
      "ScheduledMessagePage": {
        "type": "object",
        "description": "Scheduled messages and their destinations at the selected owning engine.",
        "additionalProperties": false,
        "required": [
          "engineId",
          "items"
        ],
        "properties": {
          "engineId": {
            "$ref": "#/components/schemas/ID"
          },
          "items": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/ScheduledMessageItem"
            },
            "example": []
          },
          "nextCursor": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8192,
            "description": "Omitted at the end of the list.",
            "example": "opaque-schedule-cursor"
          }
        }
      },
      "ScheduledMessageItem": {
        "type": "object",
        "description": "The current queued message, including scheduledAt and composition, and its destination.",
        "additionalProperties": false,
        "required": [
          "message",
          "conversation"
        ],
        "properties": {
          "message": {
            "x-go-type": "Message",
            "allOf": [
              {
                "$ref": "#/components/schemas/Message"
              },
              {
                "type": "object",
                "required": [
                  "scheduledAt",
                  "composition"
                ]
              }
            ]
          },
          "conversation": {
            "$ref": "#/components/schemas/Conversation"
          }
        }
      }
    }
  }
}