Messages

Chat messages with end-to-end encrypted content — text, images, files, audio, call records, and system events; inline reactions and attachments.

Service: messages.messages

A message is a single entry in a chat. The encrypted enc_content JSON holds the per-kind payload; attachments and reactions are separate child rows returned inline. Reads cover every message in every chat the caller participates in (:owner:shared); mutations are owner-only — you can edit or delete only your own messages, even in a group.

All methods use the JSON-RPC 2.0 protocol over WebSocket. See Enbox API for transport details, error codes, query operators, and encryption model.

Fields

Field Type Required Description
id string Primary key (snowflake — monotonic, so it is also the timeline order key). Read-only, auto-generated
chat_id string Parent chat id. The service verifies the caller participates in it on create
owner_id string? Sender’s user id. Read-only, auto-set on create. null for system messages
username string Sender’s login (server JOIN on accounts_user). Read-only, computed. Empty for system messages
kind string text · image · file · audio · call · system — drives the enc_content schema and the rendering
enc_content string Encrypted content JSON — see Content by kind. Plain object after decryption
owner_wrapped_key string? Message content key wrapped by the sender’s account key (base64). Required on create; null for system messages
chat_wrapped_key string? Message content key wrapped with the chat’s content key — how other participants decrypt. Set by the encryption middleware on create
reply_to_id string? Id of the message this one replies to
forwarded_from_id string? Id of the original message when forwarded
edited boolean true after an edit. Default false
created_at string ISO 8601 creation timestamp. Read-only
updated_at string ISO 8601 last update timestamp. Read-only
message_reaction MessageReaction[] Reactions on this message (read-only inline — toggling goes through the message_reactions endpoint)
message_attachment MessageAttachment[] File/image/audio metadata. Seeded inline on create

Filterable: chat_id · owner_id · kind · id (used to fetch specific last messages by id for list previews)

Note: create verifies the caller is a participant of the target chat (chat_user_role membership) — sending into a foreign chat is rejected even with a valid create:owner scope.

Content by kind

enc_content decrypts to a JSON object whose shape depends on kind:

kind Plaintext schema
text { "text": "…" }
image { "text"?: "caption" } — image data in the attachment
file {} — file metadata in the attachment
audio { "duration": "0:14" } — audio blob in the attachment
call { "call_kind": "audio" | "video", "status": "outgoing" | "missed" | "declined" | "ended", "duration"?: "1:42" }
system { "text": "Alice added Bob" } — server-generated, owner_id is null

MessageAttachment

File metadata within a message. Created inline with the parent message (one create call, one transaction); binary content then flows through the upload service via presigned URLs.

Field Type Required Description
id string Primary key. Read-only. Omit when creating
message_id string Parent message id. Read-only
size_bytes integer Logical plaintext file size in bytes
status string Upload state. Read-only. pending (uploading) · ready (finalized)
enc_name string Encrypted original file name. Plain string after decryption
enc_mime_type string Encrypted MIME type. Plain string after decryption
thumbnail_128_id string? Chunk id of the encrypted 128 px thumbnail. Read-only. null for non-image files
chunk_count integer Number of uploaded chunks. Read-only, computed
chat_wrapped_key string? Attachment content key wrapped with the chat key. Set by the encryption middleware

find

List messages — typically scoped to one chat, newest first.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "messages.messages.find",
  "params": {
    "filter": { "chat_id": { "$eq": "220226115451486210" } },
    "sort": [{ "field": "id", "dir": "asc" }],
    "limit": 200,
    "fields": [
      "id", "chat_id", "owner_id", "username", "kind",
      "owner_wrapped_key", "chat_wrapped_key",
      "reply_to_id", "forwarded_from_id", "edited",
      "enc_content", "created_at", "updated_at",
      "message_reaction.id", "message_reaction.user_id",
      "message_reaction.username", "message_reaction.reaction",
      "message_attachment.id", "message_attachment.thumbnail_128_id",
      "message_attachment.size_bytes", "message_attachment.chunk_count",
      "message_attachment.status"
    ]
  },
  "id": 1
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "items": [
      {
        "id": "220240326265143296",
        "chat_id": "220226115451486210",
        "owner_id": "214775397793923072",
        "username": "alice",
        "kind": "text",
        "owner_wrapped_key": "<base64>",
        "chat_wrapped_key": "<base64>",
        "reply_to_id": null,
        "forwarded_from_id": null,
        "edited": false,
        "enc_content": "<base64>",
        "created_at": "2026-08-31T07:58:12Z",
        "updated_at": "2026-08-31T07:58:12Z",
        "message_reaction": [
          { "id": "220240330000000001", "user_id": "214782287248621568", "username": "bob", "reaction": "heart" }
        ],
        "message_attachment": []
      }
    ],
    "total": 214
  },
  "id": 1
}

create

Send a message — optionally with its attachment row in the same transaction. The server assigns id, timestamps, and fills owner_id from the authenticated user; the caller must be a participant of the chat. After sending, clients advance their read cursor via chat.markRead.

WebSocket

Request (text)

{
  "jsonrpc": "2.0",
  "method": "messages.messages.create",
  "params": {
    "document": {
      "chat_id": "220226115451486210",
      "kind": "text",
      "enc_content": "<base64>",
      "owner_wrapped_key": "<base64>"
    }
  },
  "id": 2
}

Request (file — message + attachment in one transaction)

{
  "jsonrpc": "2.0",
  "method": "messages.messages.create",
  "params": {
    "document": {
      "chat_id": "220226115451486210",
      "kind": "file",
      "enc_content": "<base64>",
      "owner_wrapped_key": "<base64>",
      "message_attachment": [
        { "enc_name": "<base64>", "enc_mime_type": "<base64>", "size_bytes": 245760 }
      ]
    }
  },
  "id": 3
}

Response

The created message in the requested field projection (including the attachment’s assigned id with status: "pending" — continue with the upload flow).

updateOneById

Partial update — own messages only. Editing enc_content sets edited: true. Nested collections are updated automatically where present, but note that reactions are not writable through this path (see message_reactions).

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "messages.messages.updateOneById",
  "params": {
    "id": "220240326265143296",
    "document": {
      "enc_content": "<base64-new>",
      "edited": true
    }
  },
  "id": 4
}

Response

The updated message.

deleteOneById

Delete a message — own messages only (owner_id == caller), in any chat.

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "messages.messages.deleteOneById",
  "params": { "id": "220240326265143296" },
  "id": 5
}

Response

The deleted message’s final state.