Messages are the pieces of information exchanged in a chat. They are sent by a sender (a user, agent, or app integration) to one or more recipients. Messages can be of various types, including text, audio, image, and simple UI forms.
See Message.
There are two ways to send a message:
- Realtime (recommended) — over the socket connection (or the SDK). This is required if you want to receive streamed responses.
- REST — a
POST /api/messagesrequest. Useful for servers and one-off sends.
Send a message (realtime)
With the SDK, send to a known chat with proteus.chats.send, or use proteus.messages.send with a chatId (or a chatKey + recipients to start/locate a chat).
import ProteusAI from '@proteus-ai/sdk';
const proteus = new ProteusAI({ apiKey: 'user-3a4d80a9c3cca7ffd1bc341f...' });
await proteus.connect();
const chat = await proteus.chats.create({
participants: [{ id: '6819a0e484df0fc342cea506', type: 'AGENT' }],
});
await proteus.chats.join(chat.id);
// string shorthand (defaults to a TEXT message)
const { message } = await proteus.chats.send(chat.id, 'Describe this image');
// or a full payload
await proteus.chats.send(chat.id, {
content: 'Describe this image',
type: 'TEXT',
attachments: [{ url: 'https://fastly.picsum.photos/id/337/200/300.jpg' }],
});The value returned by send is your outgoing message (now persisted with an id) — not the agent's reply. Replies arrive asynchronously over the realtime connection. See Receiving replies & streaming.
Send a message (REST)
POST /api/messagescurl -L -X POST \
-H "Authorization: Bearer user-3a4d80a9c3cca7ffd1bc341f..." \
https://messaging-api.useproteus.ai/api/messages \
-d '{
"content": "Hello, how are you?",
"recipients": [
{ "id": "6819a0e484df0fc342cea506", "type": "AGENT" }
]
}'{
"ok": true,
"data": {
"id": "6832df10b749a23a30105dba",
"content": "Hello, how are you?",
"contentDelta": null,
"chatId": "6832df10b749a23a30105db7",
"chatKey": null,
"isStreaming": false,
"respondedToId": null,
"responseUrl": null,
"senderId": null,
"streamId": null,
"streamResponse": false,
"type": "TEXT",
"createdAt": "2025-05-25T09:12:48.898Z",
"updatedAt": "2025-05-25T09:12:48.898Z",
"attachments": [],
"citations": []
}
}When a message is sent without a chatId or chatKey, a chat is created automatically for it (hence the chatId in the response).
With chatId
For multi-message conversations, send subsequent messages with a chatId so they share context. No new chat is created.
{ "content": "And what about tomorrow?", "chatId": "6832df10b749a23a30105db7" }If chatId is specified, you don't need to provide recipients — all other participants of the chat are automatically the recipients. You may still pass a subset of participants in recipients.
Sending a message with a chatId that references a chat you are not a participant of fails with an error.
With chatKey
When you can't track the chatId (e.g. bridging an external chat app), pass a stable chatKey. The first send with a given chatKey creates a chat; subsequent sends with the same chatKey reuse it.
{ "content": "Hello, how are you?", "chatKey": "slack:U123:C456" }If both chatId and chatKey are specified, only chatId is used.
Receiving replies & streaming
Replies are delivered over the realtime connection to clients that have joined the chat. With the SDK:
// every complete message added to a joined chat
proteus.messages.onMessage((message) => {
console.log('message:', message.content);
});
// streamed chunks of an agent response
proteus.messages.onDelta(({ contentDelta, content }) => {
process.stdout.write(contentDelta ?? '');
});
// the final message that closes a streamed response
proteus.messages.onDone((message) => {
console.log('\ncomplete:', message.content);
});While streaming, each chunk is a message with isStreaming true and an incremental contentDelta, all sharing a streamId. The service relays these as message:delta events and emits message:done for the final message. Non-streamed replies arrive as a single message event. See Realtime for the raw events.
Here is a video of a message from an agent being streamed to a client.
Replies to a server (REST + responseUrl)
The HTTP response from POST /api/messages is not the recipient's reply. To receive a reply on a server, include a responseUrl. When a reply is ready, a WEBHOOK_MESSAGE_RECEIVED event is dispatched to that URL.
curl -L -X POST \
-H "Authorization: Bearer user-3a4d80a9c3cca7ffd1bc341f..." \
https://messaging-api.useproteus.ai/api/messages \
-d '{
"content": "Hello, how are you?",
"recipients": [{ "id": "6819a0e484df0fc342cea506", "type": "AGENT" }],
"responseUrl": "https://myserver.com/webhook"
}'For the full set of webhook events and the app-integration message flow, see the Management API messages reference.