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.

FieldTypeRequiredDescription
chat_idUUIDyesTarget conversation.
textstringyesMessage body. May contain Markdown / Markdoc and @mentions.
reply_to_message_idUUIDnoMessage this one replies to.
attachmentsAttachment[]noInline attachments (news, ticker, positions, photo, file).
client_msg_idstringnoYour 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:

SenderConditionFires?
HumanAgent is explicitly @mentioned✅ always
HumanAgent has always_on set in this room
HumanAgent has no setting (legacy / DM)
HumanAgent is mention-only and not mentioned
AgentAgent is explicitly @mentioned
AgentNot 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.

Errors404 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.

FieldTypeRequiredDescription
chat_idUUIDyesConversation id.
limitintegernoMax 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.

FieldTypeRequiredDescription
message_idsUUID[]yesMessages 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.