Menu

API reference · CRM

Messages

crm · messages · 4 operations

GET/v1/crm/conversations/{conversation_id}/messages

List the messages of a conversation

crm.messages.list

ReadIdempotentSince 2026-11-01

Lists the messages of a conversation, newest first, paginated by cursor (limit up to 200): direction, content (the text), media, template, delivery status (sent, delivered, read, or failed with error_code and error_message), who sent it (sent_by_id and sent_by_name for a member; sent_by_ai and ai_agent_user_id for an AI agent; sent_by_system and sent_by_source for a sequence or a booking notice) and its WhatsApp message id (external_id, used by crm.whatsapp.mark_read and to quote it). A template message brings template_id (the template's name) and template_variables keyed by its {{n}} (1, 2…). Attachments (a photo, a PDF, an audio) come in media_urls as links signed for one hour that download the file without a session (media_urls_expire_at, epoch ms); list again for fresh links. A link to another site (an Instagram attachment, a product image) comes as it is, and a deleted message carries no links and nothing of what it said or showed (no text, transcription or image description). The raw platform payload is not included. Same visibility as crm.conversations.get. With TikTok, their terms limit it through the API and the MCP: what they do not allow answers 403 platform_terms_restricted (the permissions guide lists what each platform allows).

Scope
crm:read
Permission
View the module · CRM action: view_inbox
MCP tool
crm_messages_listOn the full surface; on the compact one, mb_execute_operation with id: "crm.messages.list".

Declared cost: up to 205 reads and 0 writes per call (they count against the daily quota).

Parameters

conversation_idstringpathrequired

The conversation: an id from crm.conversations.list.

limitintegerqueryoptional

How many items this page brings at most (see the maximum and the default in the schema).

Default: 50

cursorstringqueryoptional

The next_cursor of the previous page, exactly as it came (only for the same list and filters); without it, the first page.

Response

dataarray<object>
has_moreboolean
next_cursorstring

Errors

Example

curl "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/messages" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/messages" \
  -H "Authorization: Bearer $MB_TEST_KEY"
GET/v1/crm/messages/count

Count the messages one person or one AI agent sent in a time window

crm.messages.count

ReadIdempotentSince 2026-11-01

Counts the CRM messages (WhatsApp and Instagram, Messenger or TikTok direct messages) one author sent between since and until (epoch ms; until is exclusive and defaults to now; at most 31 days): pass exactly one of sent_by_id, a person sending from MB Suite (the screen, MIA or a key without the AI-agent mark), or ai_agent_user_id, an AI agent (the user of a key marked as an AI agent's, or an AI Team position's ai-… user). Any channel and any CRM line; failed sends count too (an attempt is an attempt). Messages on another module's line are left out, as everywhere in the CRM. It is an aggregation, not a list: one read per 1,000 messages counted plus one for the lines (and, in an organization with lines of another module, the reads of those lines' conversations active in the window and their messages), so it is the cheap way to check a daily cap before sending (for example 40 messages per agent per day). For the load of each WhatsApp line use crm.whatsapp.sent_by_line. Anyone who reads the inbox can count their own messages; counting someone else's requires view_all_performance.

Scope
crm:read
Permission
View the module · CRM action: view_inbox
MCP tool
crm_messages_countOn the full surface; on the compact one, mb_execute_operation with id: "crm.messages.count".

Declared cost: up to 60 reads and 0 writes per call (they count against the daily quota).

Parameters

sinceintegerqueryrequired

From, epoch ms (included).

untilintegerqueryoptional

Until, epoch ms (excluded); without it, now.

ai_agent_user_idstringqueryoptional

Count what this AI agent sent: the user of a key marked as an AI agent's, or an AI Team position's ai-… user. Exactly one of ai_agent_user_id and sent_by_id.

sent_by_idstringqueryoptional

Count what this person sent from MB Suite (the screen, MIA or a key without the AI-agent mark).

Response

countnumber
sincenumber
untilnumber

Errors

Example

curl "https://api.mb-suite.com/v1/crm/messages/count?since=0" \
  -H "Authorization: Bearer $MB_API_KEY"

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl "https://api.mb-suite.com/v1/crm/messages/count?since=0" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/crm/messages/{message_id}/star

Star or unstar a message

crm.messages.star

WriteIdempotentSince 2026-11-01

Stars (starred: true) or unstars a message of a conversation, so it shows in the starred filter of the inbox. Idempotent. A seller with restricted scope only stars messages of conversations they own or on lines they work.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_messages_starOn the full surface; on the compact one, mb_execute_operation with id: "crm.messages.star".

Declared cost: up to 3 reads and 1 writes per call (they count against the daily quota).

Parameters

message_idstringpathrequired

The message: an id from crm.messages.list.

starredbooleanbodyrequired

true stars it, false removes the star.

Response

oktrue

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/messages/{message_id}/star" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "starred": true
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X POST "https://api.mb-suite.com/v1/crm/messages/{message_id}/star" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "starred": true
}'
POST/v1/crm/messages/{message_id}/react

React to a message with an emoji

crm.messages.react

WriteIdempotentSince 2026-11-01

Sets the caller's emoji reaction on a message of a conversation, replacing their previous one (one reaction per person, as in WhatsApp); an empty emoji removes it. It is an internal mark for the team inside MB Suite: it is NOT sent to WhatsApp or the contact. Idempotent: repeating the same emoji changes nothing. Returns the message's reactions after the change (emoji, user_id, user_name when known, at), the contact's own reactions from WhatsApp included. Not found if the message does not exist. A seller with restricted scope only reacts to messages of conversations they own or on lines they work.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_messages_reactOn the full surface; on the compact one, mb_execute_operation with id: "crm.messages.react".

Declared cost: up to 4 reads and 1 writes per call (they count against the daily quota).

Parameters

message_idstringpathrequired

The message: an id from crm.messages.list.

emojistringbodyrequired

One emoji: it sets (or replaces) the caller's reaction; an empty string removes it.

Response

reactionsarray<object>

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/messages/{message_id}/react" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "emoji": "EMOJI"
}'

Try it with your test API key

With a test API key the request always goes to your practice organization: example data you can reset.

curl -X POST "https://api.mb-suite.com/v1/crm/messages/{message_id}/react" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "emoji": "EMOJI"
}'