Messaging
In-app direct and group messaging between users, with typed errors and SDK examples.
Amba provides in-app messaging for direct and group conversations between users. Every endpoint returns a typed error envelope — no opaque 500s.
SDK example: 1:1 conversation
The common shape — two users, one conversation, a thread of messages:
The full messaging surface — createConversation, conversations, sendMessage, listMessages, getMessage, markRead — ships in @layers/amba-web@4.0.0+, @layers/amba-node@4.0.0+, @layers/amba-react-native@4.0.0+, @layers/amba-expo@4.0.0+, plus the native iOS, Android, Flutter, and Unity SDKs.
Live messages. Instead of polling listMessages, subscribe to a conversation and render new
messages the moment they're sent with Amba.messaging.conversation(id).subscribe(...). See Live
Updates.
Typed errors
Branch on error.code for actionable handling:
Error codes the messaging surface returns:
| Code | When |
|---|---|
INVALID_JSON | Request body wasn't parseable JSON. |
INVALID_PAYLOAD | Required field missing or malformed. error.details.missing lists the fields. |
USER_NOT_FOUND | One or more participant_ids don't exist. error.details.missing_user_ids enumerates them. |
INVALID_CONVERSATION_ID | Conversation id isn't a valid UUID. |
CONVERSATION_NOT_FOUND | Conversation doesn't exist. |
NOT_A_PARTICIPANT | Caller isn't in the conversation's participant list. |
Client API reference
| Method | Path | Description |
|---|---|---|
POST | /client/messaging/conversations | Create a conversation |
GET | /client/messaging/conversations | List user's conversations |
POST | /client/messaging/conversations/:id/messages | Send a message (optional parent_message_id + attachments) |
GET | /client/messaging/conversations/:id/messages | Get messages (paginated; ?parent_message_id filters a thread) |
POST | /client/messaging/conversations/:id/typing | Broadcast an ephemeral typing signal |
POST | /client/messaging/conversations/:id/read | Mark conversation as read |
Create a conversation
Send a message
A message can carry an optional threaded-reply parent and an optional list of
attachments alongside its body:
Threaded replies
Pass parent_message_id (a message id in the same conversation) to post a reply
that threads off another message. List a thread by filtering the message list with
?parent_message_id=<id>; omit it to list every message in the conversation. Each
message in the list response carries a reply_count — how many messages thread off
it — so you can render "12 replies" without a second call.
A parent_message_id that isn't a UUID returns 400 INVALID_PARENT; one that doesn't
reference a message in this conversation returns 404 PARENT_NOT_FOUND (you can't
reply across conversations).
Attachments
attachments is an array of { url, content_type?, size_bytes?, metadata? } — each
entry needs a non-empty url (400 INVALID_ATTACHMENTS otherwise). Pair this with
Media: upload the file, then send the message with the
returned URL. Attachments come back inline on each message in the list response as
attachments: [{ id, url, content_type, size_bytes, metadata }].
Typing signal
Broadcast an ephemeral "this user is typing" signal to the other participants over the
live connection. Nothing is persisted — it's fire-and-forget and
returns 202.
Membership-gated like send and list (403 NOT_A_PARTICIPANT otherwise).
React to a message
Add an emoji reaction (idempotent — re-adding the same emoji is a no-op), remove your own, or list the per-emoji counts. Reaction summaries also come back inline on each message in the list response as reactions: [{ emoji, count, reacted }], where reacted is whether the requesting user reacted with that emoji.
All three require the caller to be a participant in the conversation (403 NOT_A_PARTICIPANT otherwise; 404 MESSAGE_NOT_FOUND if the message isn't in that conversation).
MCP tools
| Tool | Description |
|---|---|
amba_messaging_get_stats | Get messaging statistics (total conversations, messages, active count) |