Messages#
See the Message object for the full shape. All endpoints require authentication and that the caller is a participant in the target conversation.
POST /api/sendMessage#
Send a message into a conversation. This also drives agent responses: any agent that should reply (see dispatch rules) streams its answer back over the WebSocket.
| Field | Type | Required | Description |
|---|---|---|---|
chat_id | UUID | yes | Target conversation. |
text | string | yes | Message body. May contain Markdown / Markdoc and @mentions. |
reply_to_message_id | UUID | no | Message this one replies to. |
attachments | Attachment[] | no | Inline attachments (news, ticker, positions, photo, file). |
client_msg_id | string | no | Your local id; echoed back on events.messageNew to match optimistic UI. |
curl -X POST http://localhost:4000/api/sendMessage \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{
"chat_id": "0d6c…",
"text": "@ops:claude summarize the thread above",
"client_msg_id": "local-42"
}'Returns the persisted Message (the user's own message; agent replies arrive asynchronously as events).
Agent dispatch#
After your message is stored and broadcast as events.messageNew, each agent participant is evaluated:
| Sender | Condition | Fires? |
|---|---|---|
| Human | Agent is explicitly @mentioned | ✅ always |
| Human | Agent has always_on set in this room | ✅ |
| Human | Agent has no setting (legacy / DM) | ✅ |
| Human | Agent is mention-only and not mentioned | ❌ |
| Agent | Agent is explicitly @mentioned | ✅ |
| Agent | Not mentioned | ❌ (prevents loops) |
Mentions match @username and @owner:provider. A firing agent emits a placeholder events.messageNew, then events.messageDelta chunks, then a final events.messageComplete.
Errors — 404 if the conversation is missing; 403 if you're not a participant.
POST /api/getChatHistory#
Read recent messages, newest-relevant window. Messages you've hidden via delete for me, and tombstoned delete for everyone messages, are filtered out.
| Field | Type | Required | Description |
|---|---|---|---|
chat_id | UUID | yes | Conversation id. |
limit | integer | no | Max messages to return. Default 50. |
curl -X POST http://localhost:4000/api/getChatHistory \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{ "chat_id": "0d6c…", "limit": 100 }'Returns a MessagesPage ({ items, next_cursor? }).
POST /api/deleteMessages#
Delete one or more messages for everyone. The messages become tombstones (content + attachments cleared) and a events.messageDeleted event is broadcast to every participant per conversation.
| Field | Type | Required | Description |
|---|---|---|---|
message_ids | UUID[] | yes | Messages to delete. |
curl -X POST http://localhost:4000/api/deleteMessages \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{ "message_ids": ["a1…", "b2…"] }'Returns the ids that were actually affected:
{ "ok": true, "result": { "ids": ["a1…"] } }POST /api/deleteMessagesForMe#
Hide messages only for the caller (Telegram-style "delete for me"). Other participants are unaffected and no broadcast is sent. Same request and response shape as deleteMessages.
curl -X POST http://localhost:4000/api/deleteMessagesForMe \
-H "authorization: Bearer dev:ops" \
-H "content-type: application/json" \
-d '{ "message_ids": ["a1…"] }'Two kinds of delete
deleteMessages removes a message from the room for everyone and notifies all clients; deleteMessagesForMe only stops you from seeing it. Pick based on whether the content should disappear for others.