Menu

API reference · CRM

Conversations

crm · conversations · 8 operations

GET/v1/crm/conversations

List inbox conversations

crm.conversations.list

ReadIdempotentSince 2026-11-01

Lists the CRM inbox conversations (channel whatsapp; instagram, facebook —Messenger— or tiktok direct messages; or email, the AI Setter's email bridge), most recent activity first, paginated by cursor (limit up to 200). Filter by status (open by default; closed, archived or all), channel (one of those five), WhatsApp line (phone_number_id) or the person, deal, company or prospect they are linked to. phone (with its country code) returns the WhatsApp conversations with that number on every line (one per line), matched the way the CRM matches an incoming message (the Argentine 9 and the Mexican 1 are folded), so it is the way to find the right thread before writing; an invalid number is 400 invalid_phone. last_inbound_after (epoch ms) keeps only the conversations where the contact wrote after that time (last_inbound_at), for example the replies since the last check. Conversations on a line that belongs to another part of the product are not included, and a seller with restricted scope only gets the conversations they own or those on lines they work; those filters apply to the page read, so a page can bring fewer than limit while has_more is true (follow next_cursor). Each conversation brings its contact (with contact_match_key, the key two threads of the same number share), line, linked records, the assignee and the owner with their names (assignee_name, human_owner_name), the last message and the unread count. Use crm.messages.list for the messages of one conversation. With TikTok, their terms limit it through the API and the MCP: what they do not allow answers 403 platform_terms_restricted, and their fields are left out of the response (the permissions guide lists what each platform allows).

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

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

Parameters

limitintegerqueryoptional

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

Default: 25

cursorstringqueryoptional

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

statusenumqueryoptional

open (the default), closed, archived or all.

Values: open, closed, archived, all · Default: "open"

channelenumqueryoptional

whatsapp, instagram, facebook (Messenger), tiktok or email (the email bridge of the AI Setter).

Values: whatsapp, instagram, facebook, tiktok, email

phone_number_idstringqueryoptional

Only the conversations of this WhatsApp line: a phone_number_id from crm.lines.list.

person_idstringqueryoptional

Only the conversations linked to this person (crm.persons.list). At most one of person_id, deal_id, company_id and prospect_id.

deal_idstringqueryoptional

Only the conversations linked to this deal (crm.deals.list).

company_idstringqueryoptional

Only the conversations linked to this company (crm.companies.list).

prospect_idstringqueryoptional

Only the conversations linked to this prospect (crm.prospects.list).

phonestringqueryoptional

A phone number with its country code (E.164, for example +5491155550000): only the WhatsApp conversations with that number, on any line (or on phone_number_id).

last_inbound_afterintegerqueryoptional

Only conversations whose last INBOUND message (lastInboundAt) is later than this time, epoch ms.

Response

dataarray<object>
has_moreboolean
next_cursorstring

Errors

Example

curl "https://api.mb-suite.com/v1/crm/conversations" \
  -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" \
  -H "Authorization: Bearer $MB_TEST_KEY"
GET/v1/crm/conversations/{conversation_id}

Get one conversation with its reply window

crm.conversations.get

ReadIdempotentSince 2026-11-01

Returns one inbox conversation (channel, contact, WhatsApp line, linked persons, deals, companies and prospects, owner, status, unread count, last message) plus reply_window: open (free text can be sent now), human_agent (Meta direct messages between 24 hours and 7 days: only a person replying from the screen), closed (WhatsApp more than 24 hours after the contact's last message: only an approved template) or no_window. Not found if the id does not exist, belongs to another module's line or is outside the seller's scope. With TikTok, their terms limit it through the API and the MCP: what they do not allow answers 403 platform_terms_restricted, and their fields are left out of the response (the permissions guide lists what each platform allows).

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

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

Parameters

conversation_idstringpathrequired

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

Response

idstring
channelstring

whatsapp, instagram, facebook (Messenger), tiktok or email (the email bridge of the AI Setter).

statusstring

open, closed or archived.

reply_windowobject
external_contact_idstring

The contact on the platform: their WhatsApp number without + or the social account's id.

external_contact_namestring | null
external_contact_phonestring | null

WhatsApp: the contact's number.

contact_match_keystring | null

WhatsApp: the key the CRM matches the contact's number by (the Argentine 9 and the Mexican 1 folded). Two conversations with the same key are the same number.

inbound_phone_number_idstring | null

WhatsApp: the conversation's line (an id of crm.lines.list); absent in a conversation imported from another system.

socialobject

Direct messages: the page or account they came to and the author's handle and photo.

person_idsarray<string>

The persons (crm.persons.get), the CRM records it is linked to.

deal_idsarray<string>

The deals (crm.deals.get), the CRM records it is linked to.

company_idsarray<string>

The companies (crm.companies.get), the CRM records it is linked to.

prospect_idsarray<string>

The prospects (crm.prospects.get), the CRM records it is linked to.

assignee_idstring | null

The member the conversation is assigned to.

assignee_namestring | null

That member's name (null if no longer a member).

human_owner_user_idstring | null

The person responsible for the lead behind the conversation (the owner of its open deal, prospect or person).

human_owner_namestring | null

That owner's name, as a member (null if no longer a member).

ai_owner_user_idstring | null

When an AI Team position owns the lead instead: its user (ai-<position id>, crm.ai_team.positions.list).

source_idstring | null

The lead source that was active on the line when the conversation was created (crm.lead_sources.get).

last_message_atnumber
last_message_previewstring
last_message_directionstring

inbound (the contact wrote) or outbound.

last_message_statusstring

The delivery status of the last message: sent, delivered, read or failed.

last_inbound_atnumber

When the contact last wrote (epoch ms): WhatsApp free text is allowed for 24 hours after it.

unread_countnumber
pinnedboolean
mutedboolean
label_idsarray<string>

The conversation's own labels (crm.labels.list with entity conversation).

archived_atnumber
merged_into_idstring | null

When this conversation was merged into another one: that one's id.

created_atnumber
updated_atnumber

Errors

Example

curl "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}" \
  -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}" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/crm/conversations/{conversation_id}/close

Close a conversation

crm.conversations.close

WriteIdempotentSince 2026-11-01

Closes a conversation: it leaves the open inbox until the contact writes again, a message is sent in it or it is reopened. Its messages are kept. Idempotent. A seller with restricted scope only closes conversations they own or on lines they work.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_conversations_closeOn the full surface; on the compact one, mb_execute_operation with id: "crm.conversations.close".

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

Parameters

conversation_idstringpathrequired

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

Response

oktrue

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/close" \
  -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 -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/close" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/crm/conversations/{conversation_id}/reopen

Reopen a conversation

crm.conversations.reopen

WriteIdempotentSince 2026-11-01

Reopens a closed or archived conversation so it returns to the open inbox. Idempotent. Same scope rules as crm.conversations.close.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_conversations_reopenOn the full surface; on the compact one, mb_execute_operation with id: "crm.conversations.reopen".

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

Parameters

conversation_idstringpathrequired

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

Response

oktrue

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/reopen" \
  -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 -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/reopen" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/crm/conversations/{conversation_id}/archive

Archive or unarchive a conversation

crm.conversations.archive

WriteIdempotentSince 2026-11-01

Archives a conversation, or brings it back to the open inbox with unarchive: true. An archived conversation keeps its messages and returns to the inbox by itself when the contact writes again. Idempotent. Same scope rules as crm.conversations.close.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_conversations_archiveOn the full surface; on the compact one, mb_execute_operation with id: "crm.conversations.archive".

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

Parameters

conversation_idstringpathrequired

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

unarchivebooleanbodyoptional

true takes it out of the archive (it goes back to open).

Response

oktrue

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/archive" \
  -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 -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/archive" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/crm/conversations/{conversation_id}/assign

Set the inbox assignee of a conversation

crm.conversations.assign

WriteIdempotentSince 2026-11-01

Sets the inbox assignee (assignee_id, a member of the organization) of a conversation. This is not the responsible seller shown in the inbox, which follows the owner of the linked deal, prospect or person (change that owner instead). Idempotent. A seller with restricted scope only assigns conversations they own or on lines they work.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_conversations_assignOn the full surface; on the compact one, mb_execute_operation with id: "crm.conversations.assign".

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

Parameters

conversation_idstringpathrequired

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

assignee_idstringbodyrequired

The member who takes the conversation: a user_id from crm.team.list.

Response

oktrue

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/assign" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "assignee_id": "ASSIGNEE_ID"
}'

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/conversations/{conversation_id}/assign" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "assignee_id": "ASSIGNEE_ID"
}'
POST/v1/crm/conversations/{conversation_id}/resolve-person

Find and link the CRM person of a conversation by phone

crm.conversations.resolve_person

WriteIdempotentSince 2026-11-01

Finds the CRM person whose phones include the given number (it also tries the Argentina 9 and Mexico 1 mobile variants, like the inbound webhook) and links them to the conversation if they were not linked yet. Returns person_id, or null when no person has that phone. Idempotent; it never creates persons (use crm.persons.create).

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_conversations_resolve_personOn the full surface; on the compact one, mb_execute_operation with id: "crm.conversations.resolve_person".

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

Parameters

conversation_idstringpathrequired

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

phonestringbodyrequired

The number to look the person up by, with its country code.

Response

person_idstring | null

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/conversations/{conversation_id}/resolve-person" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "phone": "PHONE"
}'

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/conversations/{conversation_id}/resolve-person" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "phone": "PHONE"
}'
POST/v1/crm/conversations

Start a WhatsApp conversation with a first text message

crm.conversations.start

WriteSince 2026-11-01

Starts a WhatsApp conversation with a phone number (E.164, with country code) by sending a first free-text message from a line (from_phone_number_id, default: the organization's first active WhatsApp line), optionally linked to a person (person_id) and a deal (deal_id). Both are checked before anything is sent: they must exist in this organization and be visible to the caller (deal_not_found or person_not_found, also for what a seller with restricted scope cannot see), a seller with restricted scope only links a deal they work (crm_action_denied with edit_deal for one they can only see), with both the person must be the deal's primary person or one of its persons (person_not_in_deal), a merged deal is deal_merged, and a merged person is replaced by the person they were merged into. If that number already has a conversation on that line it is reused and reopened (the links only go to a new conversation). Meta only delivers free text within 24 hours of the contact's last message: for a cold contact use crm.whatsapp.send_template instead. To a person marked «Do not contact» or unsubscribed it still goes out (free text only reaches a contact who wrote in the last 24 hours, so it is a reply), and the response carries do_not_contact. Requires send_whatsapp; a seller with restricted scope only starts from lines they work. From a line that belongs to another part of the product (not the CRM) it needs that part's edit permission and its write scope instead of the CRM's, and the conversation it opens or reuses belongs to that part. From such a line nothing of the CRM is linked: deal_id and person_id are refused (400 invalid_input) and the deals and persons of the CRM are not touched. With an API key marked as an AI agent's (an owner or admin sets it on the key; it is never chosen per call), the message is recorded as sent by AI, like the AI Setter's (crm.messages.list marks it as sent by AI, with the key's user as its AI agent), the AI badge in the inbox, and it counts as AI, not as a person, in reports and in caps (crm.messages.count with ai_agent_user_id). Its dry run checks the line, the recipient and the links without calling Meta, and warns when the 24-hour window of that number's conversation on that line is closed.

Needs confirmation: first dry_run: true (preview and confirm_token, valid 10 minutes and for one run), then the same body with confirm_token. In a few operations only with certain data: the description says when. Why and how: Dry run and confirm_token.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_conversations_startOn the full surface; on the compact one, mb_execute_operation with id: "crm.conversations.start".

Declared cost: up to 47 reads and 8 writes per call (they count against the daily quota). Touches an external platform (whatsapp): it counts against the external operations quota.

Parameters

phonestringbodyrequired

The contact's number with its country code (E.164, for example +5491155550000).

textstringbodyrequired

The first message, as the contact will read it.

from_phone_number_idstringbodyoptional

The WhatsApp line it goes out from: a phone_number_id from crm.lines.list. Default: the organization's first active WhatsApp line.

person_idstringbodyoptional

The person the conversation is linked to (crm.persons.list).

deal_idstringbodyoptional

The deal the conversation is linked to (crm.deals.list), with its person.

contact_namestringbodyoptional

The contact's name the inbox shows until a person is linked.

Response

conversation_idstring
message_idstring
do_not_contactobject

Only when it went out to a person marked «Do not contact» or unsubscribed: who, why, and whether it answered what they wrote after being marked or a person of the team confirmed it.

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/conversations" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "phone": "PHONE",
  "text": "TEXT",
  "dry_run": 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. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.