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:
createverifies the caller is a participant of the target chat (chat_user_rolemembership) — sending into a foreign chat is rejected even with a validcreate:ownerscope.
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.