Menu

API reference · CRM

Email

crm · email · 11 operations

POST/v1/crm/emails

Send an email from a connected mailbox

crm.email.send

WriteSince 2026-11-01

Sends an email through the mailbox the sender connected in their personal integrations (Gmail, Outlook or IMAP/SMTP) and records it as an outbound email activity linked to the given deal or person (or to the person whose email matches a recipient), so it shows in every linked timeline; a deal_id or person_id that does not exist is not_found before anything is sent. Variables {{firstName}}, {{lastName}}, {{fullName}}, {{email}}, {{company}}, {{jobTitle}}, {{dealTitle}}, {{dealValue}}, {{dealCurrency}} and {{dealStage}} in subject and body are filled in before sending. To reply inside a thread pass thread_id and reply_to_message_id, from crm.email.threads.get: that operation reads the mailbox content, so an API key or a third-party app has it only when the organization's owner allows connected mailbox content and the credential was granted it (MIA never reads it); without it, send a new email. sender_user_id sends from another member's mailbox (owners, admins and CRM admins only); the mailboxes to send from, their ids and aliases are in crm.email.senders.list. from_alias must be an alias enabled for that mailbox (not available for Outlook). Attachments must already be stored under the organization's email attachments (uploading files is not available through the API). The dry run checks what the send checks before going out (the sender's mailbox, the alias, the deal or person, the attachments' place) without calling the mail provider, and warns about variables that go out as written or empty and about an email that no timeline will show. Not idempotent: retrying sends the email again; when the mail provider does not confirm the send it answers external_outcome_unknown (the email may have gone out: check the sent folder before sending it again). A person marked «Do not contact» or unsubscribed gets nothing that starts the contact: when a recipient (to or cc, by any of the person's emails) is one of them, nothing is sent and the answer is 409 do_not_contact, with the addresses and why in details.recipients and whether it can be confirmed in details.overridable. Only a person of the team can confirm it, on a closed basis (do_not_contact_override: about their account, a charge or support, or because the person asked us to write), with a reason outside the app (3 to 500 characters; without it, 400 do_not_contact_reason_required); it is recorded in the audit log and on the activity. An AI agent's API key cannot confirm it (403 do_not_contact_override_not_allowed, and its 409 says overridable false). A reply in a thread (thread_id) where that person wrote last, after being marked (within the last 24 hours for a mark from before 2026-10-16), goes out without confirmation. When the email goes out to one of them, the response carries do_not_contact: the recipients, allowed_as (reply or override) and, when confirmed, the basis. The dry run gives the same answer or says why it goes out anyway. Requires edit access to the CRM and the send_email permission (owners, admins and members; never viewers). In the practice organization (test keys) nothing is sent: the outbound activity is recorded as simulated and the response carries simulated: true.

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 · CRM action: send_email
MCP tool
crm_email_sendOn the full surface; on the compact one, mb_execute_operation with id: "crm.email.send".

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

Parameters

toarray<string (email)>bodyrequired

The recipients' addresses (1 to 50).

subjectstringbodyrequired

The subject, as plain text; the {{…}} variables are filled in.

body_htmlstringbodyrequired

The body in HTML; the {{…}} variables are filled in. The signature is not added on its own (crm.email.signatures.list).

ccarray<string (email)>bodyoptional

The addresses in copy (up to 20).

deal_idstringbodyoptional

The deal the email is recorded on (crm.deals.list): it also links its persons and company, and fills the deal variables. It must exist.

person_idstringbodyoptional

The person the email is recorded on (crm.persons.list), when there is no deal: it also links their company and fills the person variables. It must exist.

linksobjectbodyoptional

Only when the deal or person has several linked records and only some of them must show the email in their timeline: the subset to link (ids that are not candidates are ignored; the deal or person itself is always linked). Without it, every candidate.

thread_idstringbodyoptional

To reply inside a thread: its thread_id, with reply_to_message_id (both from crm.email.threads.get). Without it, a new email.

reply_to_message_idstringbodyoptional

The message_id of the message this replies to, with thread_id (crm.email.threads.get).

sender_user_idstringbodyoptional

The member whose mailbox sends it: the id of a sender of crm.email.senders.list. Without it, the caller's own mailbox. Sending from another member's mailbox is for owners, admins and CRM admins only.

from_aliasstring (email)bodyoptional

A send-as alias enabled for the sender's mailbox (its aliases in crm.email.senders.list, with can_send true). Without it, the mailbox address. Not available for Outlook.

attachmentsarray<object>bodyoptional

Up to 10 files already stored under the organization's email attachments, 25 MB each (uploading files is not available through the API).

do_not_contact_overrideobjectbodyoptional

Only for a recipient marked «Do not contact» or unsubscribed (409 do_not_contact names them): a person of the team confirms the message, on one of two bases. An AI agent's API key cannot confirm it (403 do_not_contact_override_not_allowed) and a WhatsApp marketing template never goes to them. Without it nothing is sent to them; a reply to what they wrote after being marked needs no confirmation (each send says when).

Response

message_idstring

The provider's id of the sent message.

thread_idstring

The provider's id of its thread.

activity_idstring

The outbound email activity recorded in the CRM (crm.activities.get).

do_not_contactobject

Only when the email 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/emails" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": [
    "TO"
  ],
  "subject": "SUBJECT",
  "body_html": "BODY_HTML",
  "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 simulated: nothing is sent and the response carries simulated: true.

curl -X POST "https://api.mb-suite.com/v1/crm/emails" \
  -H "Authorization: Bearer $MB_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": [
    "TO"
  ],
  "subject": "SUBJECT",
  "body_html": "BODY_HTML",
  "dry_run": true
}'
GET/v1/crm/emails/senders

List the mailboxes the caller can send from, and which need reconnecting

crm.email.senders.list

ReadIdempotentSince 2026-11-01

Lists the mailboxes to send from: the connected ones the caller can send an email from, their own, or every active mailbox of the organization for owners, admins and CRM admins. The mailboxes the CRM reads and syncs (the email inbox, by privacy) are in crm.email.mailboxes.list: the same connections, seen for reading. Each sender's id is the sender_user_id to pass to crm.email.send, with the send-as aliases enabled for that mailbox (can_send is false for Outlook aliases), status connected and ses_fallback (the address is also a verified SES sender of the organization, so a sequence keeps sending through it if the mailbox connection drops; otherwise its contacts pause with gmail_connection_lost). default_sender_id is the caller's own mailbox when it is connected, otherwise the first of the list (null when there is none). needs_reconnect lists, with the same reach, the mailboxes whose connection dropped (the provider rejected the stored token): nothing is sent from them, sequences included, until their owner reconnects them in Integrations in the app (there is no API for that); provider says which one (google_gmail, microsoft_outlook, imap_smtp) and since says when it was marked, if known. caller_mailbox is the caller's own mailbox: connected, reconnect_required or not_connected. Read from what MB Suite stores: it does not call Google or Microsoft. Requires view access to the CRM.

Scope
crm:read
Permission
View the module
MCP tool
crm_email_senders_listOn the full surface; on the compact one, mb_execute_operation with id: "crm.email.senders.list".

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

Parameters

No parameters.

Response

dataarray<object>
default_sender_idstring | null

The caller's own mailbox when it is connected, otherwise the first of the list (null when there is none).

needs_reconnectarray<object>

The mailboxes whose connection dropped: nothing is sent from them until their owner reconnects them.

caller_mailboxenum

The state of the caller's own mailbox.

Values: connected, reconnect_required, not_connected

Errors

Example

curl "https://api.mb-suite.com/v1/crm/emails/senders" \
  -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/emails/senders" \
  -H "Authorization: Bearer $MB_TEST_KEY"
GET/v1/crm/emails/signatures

List the email signatures of a CRM user

crm.email.signatures.list

ReadIdempotentSince 2026-11-01

Returns the email signatures (name, HTML, which one is the default) of a CRM user: the caller's own by default, or another member's (user_id) for owners, admins and CRM admins. Append the default one at the end of the body of crm.email.send when the user expects their signature. Signatures are created and edited from the screen only.

Scope
crm:read
Permission
View the module
MCP tool
crm_email_signatures_listOn the full surface; on the compact one, mb_execute_operation with id: "crm.email.signatures.list".

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

Parameters

user_idstringqueryoptional

The member whose signatures to read (owners, admins and CRM admins only). Without it, the caller's own.

Response

dataarray<object>

Errors

Example

curl "https://api.mb-suite.com/v1/crm/emails/signatures" \
  -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/emails/signatures" \
  -H "Authorization: Bearer $MB_TEST_KEY"
GET/v1/crm/emails/mailboxes

List the mailboxes the CRM reads and syncs

crm.email.mailboxes.list

ReadIdempotentSince 2026-11-01

Lists the mailboxes to read and sync: the connected mailboxes (Gmail, Outlook or IMAP) of the CRM team whose emails become CRM activities and make the email inbox, the ones the caller is allowed to read, with the owner's user_id and name, the mailbox address, its status (a dropped one stops syncing), last sync and privacy mode. Private mailboxes of other users are left out (owners, admins and CRM admins see all); an API key, a third-party app and MIA see only the mailbox of the user they act as. To send an email, the mailboxes to send from are in crm.email.senders.list: the same connections, seen for sending (only the active ones, with their aliases). The user_id is the owner_user_id of crm.email.mailbox.sync and of the crm.email.threads operations; those read the mailbox content, so an API key or a third-party app has them only when the organization's owner allows connected mailbox content and the credential was granted it (MIA never has them).

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

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

Parameters

No parameters.

Response

dataarray<object>

Errors

Example

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

List the email threads of a mailbox

crm.email.threads.list

ReadIdempotentConnected mailboxes and calendarsSince 2026-11-01

For API keys and third-party apps this operation exists only when the organization's owner allows connected mailbox content and the credential was granted it; even then it returns only threads linked to a CRM person, company or deal (person_id, company_id or deal_id present), never the whole mailbox. Lists the threads of one mailbox (owner_user_id, from crm.email.mailboxes.list) in a folder: inbox (default), sent or archived; a thread you replied to lives in both inbox and sent. Newest first, paginated by cursor (limit up to 200), built from the mailbox's latest 500 emails (up to 200 threads). Each thread has its subject, last snippet, participants, message and unread counts and the person, deal or company it is linked to. A private mailbox, or one that does not exist, returns an empty list. Use crm.email.threads.get for the messages.

Only if the organization allows it and the credential has it granted; it returns only what is linked to the CRM from the caller's own mailbox. Connected mailboxes and calendars

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

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

Parameters

owner_user_idstringpathrequired

The member who owns the mailbox: the user_id of crm.email.mailboxes.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.

folderenumqueryoptional

The folder: inbox (the default), sent or archived.

Values: inbox, sent, archived · Default: "inbox"

Response

dataarray<object>
has_moreboolean
next_cursorstring

Errors

Example

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

Get the messages of an email thread

crm.email.threads.get

ReadIdempotentConnected mailboxes and calendarsSince 2026-11-01

For API keys and third-party apps this operation exists only when the organization's owner allows connected mailbox content and the credential was granted it; a thread with no link to a CRM person, company or deal is not found for them. Returns the messages of a thread of a mailbox, oldest first: direction, from, to, cc, subject, the sanitized HTML body, a text preview, the date, the linked CRM ids (linked_person_ids, linked_deal_ids, linked_company_ids), the provider message_id (pass it as reply_to_message_id with the thread_id to reply through crm.email.send) and the attachments. Not found if the thread is not in the mailbox's latest emails or the mailbox is private to the caller.

Only if the organization allows it and the credential has it granted; it returns only what is linked to the CRM from the caller's own mailbox. Connected mailboxes and calendars

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

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

Parameters

owner_user_idstringpathrequired

The member who owns the mailbox: the user_id of crm.email.mailboxes.list.

thread_idstringpathrequired

The thread: the thread_id of crm.email.threads.list.

Response

dataarray<object>

Errors

Example

curl "https://api.mb-suite.com/v1/crm/emails/mailboxes/{owner_user_id}/threads/{thread_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/emails/mailboxes/{owner_user_id}/threads/{thread_id}" \
  -H "Authorization: Bearer $MB_TEST_KEY"
POST/v1/crm/emails/mailboxes/{owner_user_id}/threads/{thread_id}/archive

Archive an email thread

crm.email.threads.archive

WriteIdempotentConnected mailboxes and calendarsSince 2026-11-01

Archives a thread of a mailbox: it leaves the inbox for everyone who reads that mailbox and moves to the archived folder; for Gmail mailboxes it is also archived in Gmail (best effort). It deletes nothing; crm.email.threads.unarchive brings it back. Idempotent. Requires being able to read the mailbox.

Only if the organization allows it and the credential has it granted; it returns only what is linked to the CRM from the caller's own mailbox. Connected mailboxes and calendars

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

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

Parameters

owner_user_idstringpathrequired

The member who owns the mailbox: the user_id of crm.email.mailboxes.list.

thread_idstringpathrequired

The thread: the thread_id of crm.email.threads.list.

Response

updatednumber

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/emails/mailboxes/{owner_user_id}/threads/{thread_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. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.

POST/v1/crm/emails/mailboxes/{owner_user_id}/threads/{thread_id}/unarchive

Move an archived email thread back to the inbox

crm.email.threads.unarchive

WriteIdempotentConnected mailboxes and calendarsSince 2026-11-01

Moves an archived thread of a mailbox back to the inbox for everyone who reads that mailbox (and back to the Gmail inbox, best effort). Idempotent. Requires being able to read the mailbox.

Only if the organization allows it and the credential has it granted; it returns only what is linked to the CRM from the caller's own mailbox. Connected mailboxes and calendars

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_email_threads_unarchiveOn the full surface; on the compact one, mb_execute_operation with id: "crm.email.threads.unarchive".

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

Parameters

owner_user_idstringpathrequired

The member who owns the mailbox: the user_id of crm.email.mailboxes.list.

thread_idstringpathrequired

The thread: the thread_id of crm.email.threads.list.

Response

updatednumber

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/emails/mailboxes/{owner_user_id}/threads/{thread_id}/unarchive" \
  -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. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.

POST/v1/crm/emails/mailboxes/{owner_user_id}/threads/bulk/archive

Archive many email threads of a mailbox

crm.email.threads.archive.bulk

WriteBulk · up to 50IdempotentConnected mailboxes and calendarsSince 2026-11-01

Archives up to 50 threads of one mailbox at once (same effect as archiving them one by one, also in Gmail, best effort). Requires being able to read the mailbox. Bulk: outside the screen it requires a dry run and a confirm_token.

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.

Only if the organization allows it and the credential has it granted; it returns only what is linked to the CRM from the caller's own mailbox. Connected mailboxes and calendars

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

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

Parameters

owner_user_idstringpathrequired

The member who owns the mailbox: the user_id of crm.email.mailboxes.list.

thread_idsarray<string>bodyrequired

The threads to archive (1 to 50), thread_ids of crm.email.threads.list.

Response

updatednumber
thread_countnumber

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/emails/mailboxes/{owner_user_id}/threads/bulk/archive" \
  -H "Authorization: Bearer $MB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "thread_ids": [
    "THREAD_ID"
  ],
  "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.

POST/v1/crm/emails/mailboxes/{owner_user_id}/sync

Sync a mailbox now

crm.email.mailbox.sync

WriteIdempotentSince 2026-11-01

Pulls the new messages of a mailbox from its provider (Gmail, Outlook or IMAP) since its last sync (the last 90 days the first time, up to 2,000 messages) and records the ones that match CRM persons or companies as email activities, with the same deduplicated ingestion as the scheduled sync: running it again never duplicates. Returns how many were fetched, recorded and skipped (and why). It can take tens of seconds. Requires being able to read the mailbox.

Scope
crm:write
Permission
Edit in the module
MCP tool
crm_email_mailbox_syncOn the full surface; on the compact one, mb_execute_operation with id: "crm.email.mailbox.sync".

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

Parameters

owner_user_idstringpathrequired

The member who owns the mailbox: the user_id of crm.email.mailboxes.list.

Response

ingestednumber
skippednumber
linked_to_dealsnumber
fetchednumber
since_msnumber
skipped_reasonsobject

Errors

Example

curl -X POST "https://api.mb-suite.com/v1/crm/emails/mailboxes/{owner_user_id}/sync" \
  -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. In the practice organization this operation is not simulated (it reaches another platform): it answers 409 sandbox_external_unavailable.