Conversations & Messages
Conversations are the per-contact message threads in your SMBcrm account. Every SMS, email, or other channel message to or from a contact belongs to one. Use these endpoints to search and read conversations, send or record the messages inside them, export message history, upload attachments, and fetch call recordings and transcripts.
Base URL: https://services.smbcrm.com · Version header: v3 ·
Scopes: conversations.readonly, conversations.write, conversations/message.readonly,
conversations/message.write. See Scopes.
Search conversations
Section titled “Search conversations”locationId is required. Every other parameter is optional.
| Query param | Type | Required | Description |
|---|---|---|---|
locationId |
string | Yes | Your SMBcrm account/location ID. |
contactId |
string | No | Only conversations with this contact. |
query |
string | No | Free-text search string. |
assignedTo |
string | No | Comma-separated user IDs the conversations are assigned to. Use unassigned for conversations with no assignee. |
followers |
string | No | Comma-separated IDs of users who follow the conversation. |
mentions |
string | No | Comma-separated IDs of mentioned users. |
status |
string | No | One of all, read, unread, starred, or recents. |
lastMessageType |
string | No | Only conversations whose last message has this type, for example TYPE_SMS or TYPE_EMAIL. |
lastMessageDirection |
string | No | inbound or outbound. Filters on the direction of the last message. |
lastMessageAction |
string | No | automated or manual. Filters on the last outbound message. |
sort |
string | No | asc or desc. |
sortBy |
string | No | One of last_message_date, last_manual_message_date, or score_profile. |
sortScoreProfile |
string | No | ID of the score profile to sort on when sortBy is score_profile. |
scoreProfile |
string | No | ID of a score profile to filter by. Use with scoreProfileMin and scoreProfileMax. |
scoreProfileMin, scoreProfileMax |
number | No | Minimum and maximum score for the scoreProfile filter. |
limit |
number | No | Number of conversations to return. Default 20. |
startAfterDate |
number or array | No | Start the results after this sort value. Pass the sort value of the last conversation on the previous page. |
id |
string | No | ID of a conversation. |
The response includes total, the number of conversations that match the query.
curl "https://services.smbcrm.com/conversations/search?locationId=<location_id>&contactId=<contact_id>&status=unread&sort=desc&sortBy=last_message_date" \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "conversations": [ { "id": "<conversation_id>", "contactId": "<contact_id>", "locationId": "<location_id>", "lastMessageBody": "See you at 3pm!", "lastMessageType": "TYPE_SMS", "type": "TYPE_PHONE", "unreadCount": 0, "fullName": "Jordan Lee", "contactName": "Jordan Lee", "email": "jordan@example.com", "phone": "+15551234567" } ], "total": 1}Retrieve a conversation
Section titled “Retrieve a conversation”type is a numeric channel code here, not the string form that search
returns.
| Code | String form in search results | Channel |
|---|---|---|
1 |
TYPE_PHONE |
Phone |
2 |
TYPE_EMAIL |
|
3 |
TYPE_FB_MESSENGER |
Facebook Messenger |
4 |
TYPE_REVIEW |
Review |
5 |
TYPE_GROUP_SMS |
Group SMS |
assignedTo is the ID of the team member handling the conversation, when the conversation has
an assignee.
curl https://services.smbcrm.com/conversations/<conversation_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "id": "<conversation_id>", "locationId": "<location_id>", "contactId": "<contact_id>", "assignedTo": "<user_id>", "type": 1, "unreadCount": 0, "inbox": true, "deleted": false, "starred": false}Create, update & delete a conversation
Section titled “Create, update & delete a conversation”locationId and contactId are both required. Use this to start an empty conversation thread
with a contact before you have a message to send.
curl -X POST https://services.smbcrm.com/conversations/ \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "locationId": "<location_id>", "contactId": "<contact_id>" }'{ "success": true, "conversation": { "id": "<conversation_id>", "locationId": "<location_id>", "contactId": "<contact_id>", "dateAdded": "2026-07-08T15:04:00.000Z", "dateUpdated": "2026-07-08T15:04:00.000Z", "lastMessageDate": "2026-07-08T15:04:00.000Z", "deleted": false }}locationId is required in the body. The path carries only the conversation ID. The optional
body fields are unreadCount and starred.
curl -X PUT https://services.smbcrm.com/conversations/<conversation_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "locationId": "<location_id>", "starred": true, "unreadCount": 0 }'{ "success": true, "conversation": { "id": "<conversation_id>", "locationId": "<location_id>", "contactId": "<contact_id>", "assignedTo": "<user_id>", "userId": "<user_id>", "lastMessageBody": "See you at 3pm!", "lastMessageDate": "1783523040000", "lastMessageType": "TYPE_SMS", "unreadCount": 0, "inbox": true, "starred": true, "deleted": false }}curl -X DELETE https://services.smbcrm.com/conversations/<conversation_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "success": true }Messages
Section titled “Messages”Each message object includes both a numeric type and a readable messageType (for example
TYPE_SMS). Use messageType unless you need the raw code. The common codes are:
type |
messageType |
|---|---|
1 |
TYPE_CALL |
2 |
TYPE_SMS |
3 |
TYPE_EMAIL |
10 |
TYPE_CAMPAIGN_VOICEMAIL |
11 |
TYPE_FACEBOOK |
18 |
TYPE_INSTAGRAM |
19 |
TYPE_WHATSAPP |
A message object has these fields. id, type, messageType, locationId, contactId,
conversationId, dateAdded, direction, and contentType are always present. The rest
appear when they apply.
| Field | Type | Description |
|---|---|---|
id |
string | Message ID. |
type |
number | Numeric message type. See the table above. |
messageType |
string | Readable message type, for example TYPE_SMS. |
direction |
string | inbound or outbound. |
status |
string | One of connected, delivered, failed, opened, pending, read, scheduled, sent, undelivered, clicked, or opt_out. |
body |
string | Message text. |
contentType |
string | Content type of the body, for example text/plain. |
attachments |
array | Attachment URLs. Empty for calls and voicemails; fetch their audio from the recording endpoint. |
meta |
object | Channel details: callDuration (seconds, as a string) and callStatus for calls, email.messageIds (every email message ID in the thread) for emails, and the page details in fb and ig for Facebook and Instagram messages. callStatus is one of pending, completed, answered, busy, no-answer, failed, canceled, or voicemail. |
source |
string | Where the message came from: workflow, bulk_actions, campaign, api, or app. |
userId |
string | ID of the team member who sent the message. |
from, to |
string | Sender and recipient (a phone number or name). Not returned for email messages. |
error |
string | Delivery failure text, when the message failed. |
altId |
string | Alternate message ID from an external provider. |
conversationProviderId |
string | ID of the conversation provider the message went through. |
chatWidgetId |
string | ID of the chat widget the message came from. |
| Query param | Type | Required | Description |
|---|---|---|---|
limit |
number | No | Number of messages to return. Default 20. |
lastMessageId |
string | No | The messages.lastMessageId from the previous response. Pass it to fetch the next page. |
type |
string | No | Comma-separated message types to return, for example TYPE_SMS,TYPE_CALL. |
type accepts TYPE_CALL, TYPE_SMS, TYPE_EMAIL, TYPE_FACEBOOK, TYPE_GMB,
TYPE_INSTAGRAM, TYPE_WHATSAPP, TYPE_ACTIVITY_APPOINTMENT, TYPE_ACTIVITY_CONTACT,
TYPE_ACTIVITY_INVOICE, TYPE_ACTIVITY_PAYMENT, TYPE_ACTIVITY_OPPORTUNITY,
TYPE_LIVE_CHAT, TYPE_INTERNAL_COMMENTS, and TYPE_ACTIVITY_EMPLOYEE_ACTION_LOG.
curl "https://services.smbcrm.com/conversations/<conversation_id>/messages?limit=20&type=TYPE_SMS,TYPE_CALL" \ -H "Authorization: Bearer <token>" \ -H "Version: v3"The response nests the list. The message array is at messages.messages, and the paging fields
lastMessageId and nextPage sit beside it inside messages.
{ "messages": { "lastMessageId": "<message_id>", "nextPage": false, "messages": [ { "id": "<message_id>", "type": 2, "messageType": "TYPE_SMS", "locationId": "<location_id>", "contactId": "<contact_id>", "conversationId": "<conversation_id>", "dateAdded": "2026-07-08T15:04:00.000Z", "body": "See you at 3pm!", "direction": "outbound", "status": "delivered", "contentType": "text/plain" } ] }}When nextPage is true, more messages remain. Send lastMessageId back as the lastMessageId
query parameter to get the next page, and repeat until nextPage is false.
const all = [];let lastMessageId;
do { const url = new URL('https://services.smbcrm.com/conversations/<conversation_id>/messages'); url.searchParams.set('limit', '20'); if (lastMessageId) url.searchParams.set('lastMessageId', lastMessageId);
const res = await fetch(url, { headers: { Authorization: 'Bearer <token>', Version: 'v3' }, }); const { messages } = await res.json();
all.push(...messages.messages); lastMessageId = messages.nextPage ? messages.lastMessageId : undefined;} while (lastMessageId);This response is the message object itself, not wrapped in a messages key.
curl https://services.smbcrm.com/conversations/messages/<message_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "id": "<message_id>", "type": 2, "messageType": "TYPE_SMS", "locationId": "<location_id>", "contactId": "<contact_id>", "conversationId": "<conversation_id>", "dateAdded": "2026-07-08T15:04:00.000Z", "body": "See you at 3pm!", "direction": "outbound", "status": "delivered", "contentType": "text/plain"}Get an email message
Section titled “Get an email message”Use the emailMessageId returned when you sent the email, or an ID from
meta.email.messageIds on a message object.
id, threadId, locationId, contactId, conversationId, dateAdded, body, direction,
contentType, from, and to are always present. The rest appear when they apply.
| Field | Type | Description |
|---|---|---|
from |
string | Name and email address of the sender. |
to |
array | Email addresses of the recipients. |
direction |
string | inbound or outbound. |
subject |
string | Subject line. |
status |
string | One of pending, scheduled, sent, delivered, read, undelivered, connected, failed, or opened. |
cc, bcc |
array | Email addresses in the CC and BCC fields. |
attachments |
array | Attachment URLs. |
replyToMessageId |
string | For a reply, the email message ID of the email being replied to. |
source |
string | workflow, bulk_actions, campaign, api, or app. |
provider, conversationProviderId, altId |
string | The provider the email went through, its conversation provider ID, and the external ID. |
error |
string | Bounce or failure text for emails that failed. |
curl https://services.smbcrm.com/conversations/messages/email/<email_message_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "id": "<email_message_id>", "threadId": "<thread_id>", "locationId": "<location_id>", "contactId": "<contact_id>", "conversationId": "<conversation_id>", "dateAdded": "2026-07-08T15:04:00.000Z", "subject": "Confirming our call today", "body": "<p>Hey Jordan, just confirming our call at 3pm today.</p>", "direction": "outbound", "status": "delivered", "contentType": "text/html", "from": "Sam Rivera <sam@example.com>", "to": ["jordan@example.com"]}Export messages
Section titled “Export messages”Use this to pull message history across conversations. Each message is the standard message object.
| Query param | Type | Required | Description |
|---|---|---|---|
locationId |
string | Yes | Your SMBcrm account/location ID. |
channel |
string | No | One of Call, SMS, Email, WhatsApp, Instagram, or Facebook. |
limit |
number | No | Messages per page. Default 100, minimum 10, maximum 1000. |
cursor |
string | No | The nextCursor from the previous response. |
sortBy |
string | No | createdAt or updatedAt. Default createdAt. |
sortOrder |
string | No | asc or desc. Default desc. |
conversationId |
string | No | Only messages in this conversation. |
contactId |
string | No | Only messages for this contact. |
startDate, endDate |
string | No | Start and end of the date range to export. |
Without channel, the export returns every non-email message type, including activity
messages such as opportunity updates and appointments. Set channel=Email to get emails. Group
chat and SMS review request messages are not supported.
A cursor stays valid for 2 minutes after your last request. Pass each response’s nextCursor
as the next request’s cursor until nextCursor is null.
curl "https://services.smbcrm.com/conversations/messages/export?locationId=<location_id>&channel=SMS&limit=100" \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "messages": [ { "id": "<message_id>", "type": 2, "messageType": "TYPE_SMS", "locationId": "<location_id>", "contactId": "<contact_id>", "conversationId": "<conversation_id>", "dateAdded": "2026-07-08T15:04:00.000Z", "body": "See you at 3pm!", "direction": "outbound", "status": "delivered", "contentType": "text/plain" } ], "nextCursor": "<cursor>", "total": 1234}total is the number of messages that match the query.
Call recordings & transcripts
Section titled “Call recordings & transcripts”Call and voicemail messages have an empty attachments array. Fetch the audio and the
transcript with these endpoints, using the message’s id and your locationId.
The response is the audio file, returned as an audio/x-wav attachment named audio.wav.
Save it with -o.
curl https://services.smbcrm.com/conversations/messages/<message_id>/locations/<location_id>/recording \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -o audio.wavEach transcript sentence has the fields below. startTime and endTime are in milliseconds.
| Field | Type | Description |
|---|---|---|
mediaChannel |
number | The audio channel the sentence was spoken on. |
sentenceIndex |
number | Position of the sentence in the transcript. |
startTime, endTime |
number | When the sentence starts and ends, in milliseconds. |
transcript |
string | The text of the sentence. |
confidence |
number | Confidence of the transcription. |
curl https://services.smbcrm.com/conversations/locations/<location_id>/messages/<message_id>/transcription \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "mediaChannel": 1, "sentenceIndex": 1, "startTime": 34, "endTime": 45, "transcript": "This call may be recorded for quality assurance purposes.", "confidence": 0.5}The response is a text/plain attachment named transcription.txt.
curl https://services.smbcrm.com/conversations/locations/<location_id>/messages/<message_id>/transcription/download \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -o transcription.txtSend a message
Section titled “Send a message”type, contactId, and status are required. Set type to the channel you are sending on.
The rest of the body depends on that channel.
| Body field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | One of SMS, Email, WhatsApp, IG, FB, Custom, Live_Chat, or InternalComment. |
contactId |
string | Yes | The contact receiving the message. |
status |
string | Yes | Message status. One of delivered, failed, pending, or read. |
message |
string | No | Text content. For InternalComment, this is the comment text. |
html |
string | No | HTML content, for email. |
subject |
string | No | Subject line, for email. |
attachments |
array | No | Attachment URLs. Get them from Upload attachments. |
fromNumber, toNumber |
string | No | Sender and recipient phone numbers for SMS. |
emailFrom |
string | No | Address to send the email from. |
emailTo |
string | No | Recipient address, if it differs from the contact’s primary email. It should be an address associated with the contact. |
emailCc, emailBcc |
array | No | Email addresses to copy or blind copy. |
emailReplyMode |
string | No | reply or reply_all. |
replyMessageId |
string | No | ID of the message you are replying to. |
threadId |
string | No | ID of the message thread. For email, the message ID that groups the emails in the thread. |
templateId |
string | No | ID of a message template. |
appointmentId |
string | No | ID of an appointment to associate with the message. |
scheduledTimestamp |
number | No | UTC timestamp in seconds. Schedules the message to send then. See Schedule and cancel a message. |
conversationProviderId |
string | No | ID of the conversation provider. |
whatsapp |
object | No | WhatsApp media payload. Applies only when type is WhatsApp. |
mentions |
array | No | User IDs mentioned in the comment. Required when type is InternalComment. |
userId |
string | No | Author of the comment when type is InternalComment. |
curl -X POST https://services.smbcrm.com/conversations/messages \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "type": "SMS", "contactId": "<contact_id>", "status": "pending", "message": "Hey Jordan, just confirming our call at 3pm today." }'curl -X POST https://services.smbcrm.com/conversations/messages \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "type": "Email", "contactId": "<contact_id>", "status": "pending", "subject": "Confirming our call today", "html": "<p>Hey Jordan, just confirming our call at 3pm today.</p>" }'Set whatsapp.type to media, then give whatsapp.media a type (image, video, audio,
or document), a url, and a name. The url must be publicly reachable. Use the URL
that Upload attachments returns. Leave whatsapp out for a text-only message.
curl -X POST https://services.smbcrm.com/conversations/messages \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "type": "WhatsApp", "contactId": "<contact_id>", "status": "pending", "message": "Here is the quote we talked about.", "whatsapp": { "type": "media", "media": { "type": "document", "url": "<attachment_url>", "name": "quote.pdf" } } }'An InternalComment posts a note that only your team sees. Mention a team member in message
with @username<userId>actualUserId</userId>, and list the same user IDs in mentions.
curl -X POST https://services.smbcrm.com/conversations/messages \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "type": "InternalComment", "contactId": "<contact_id>", "status": "pending", "message": "@Sam<userId><user_id></userId> can you call Jordan back today?", "mentions": ["<user_id>"] }'The response always has conversationId and messageId:
{ "conversationId": "<conversation_id>", "messageId": "<message_id>"}For Email sends, the response also has emailMessageId. Use it to
fetch the email or to thread inbound replies.
The response doesn’t include a delivery status. To check on it, fetch the message
by its ID and read status.
Schedule and cancel a message
Section titled “Schedule and cancel a message”Set scheduledTimestamp on Send a message to a UTC timestamp in seconds, and
the message sends at that time. For example, 1792072800 is October 15, 2026 at 14:00 UTC.
curl -X POST https://services.smbcrm.com/conversations/messages \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "type": "SMS", "contactId": "<contact_id>", "status": "pending", "message": "Reminder: your appointment is tomorrow at 3pm.", "scheduledTimestamp": 1792072800 }'Cancel a scheduled message before it sends. Messages and emails have separate cancel endpoints.
Both return status (the HTTP status code of the request) and message (a result message).
Use the messageId that the send response returned when you scheduled the message.
curl -X DELETE https://services.smbcrm.com/conversations/messages/<message_id>/schedule \ -H "Authorization: Bearer <token>" \ -H "Version: v3"Use the emailMessageId that the send response returned when you scheduled the email.
curl -X DELETE https://services.smbcrm.com/conversations/messages/email/<email_message_id>/schedule \ -H "Authorization: Bearer <token>" \ -H "Version: v3"Upload attachments
Section titled “Upload attachments”Send the request as multipart/form-data, with each file under the key fileAttachment. The
response has an uploadedFiles object with the URLs of the stored files. Pass those URLs in
attachments when you send a message, or in whatsapp.media.url for
WhatsApp media.
| Form field | Required | Description |
|---|---|---|
locationId |
Yes | Your SMBcrm account/location ID. |
conversationId, contactId, workflowId, campaignId |
One of them | The conversation, contact, workflow, or campaign the files belong to. |
fileAttachment |
Yes | The file to upload. |
isSecureAttachment |
No | true or false. Default false. See below. |
Each file can be up to 5 MB, and one upload can hold up to 5 files. These file types are allowed:
| Category | Types |
|---|---|
| Images | JPG, JPEG, PNG, GIF, SVG, HEIC, AI |
| Videos | MP4, MPEG, 3GP |
| Audio | MP3, WAV, WAVE, AIFF, AIF, AIFC, GSM, ULAW, OGG, AAC, M4A, AMR |
| Documents | PDF, DOC, DOCX, TXT, CSV, XLS, XLSX, PPT, PPTX, ODT |
| Archives | ZIP, RAR |
| Other | VCF, VCARD (contact files), ICS (calendar files) |
curl -X POST https://services.smbcrm.com/conversations/messages/upload \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -F "locationId=<location_id>" \ -F "contactId=<contact_id>" \ -F "fileAttachment=@quote.pdf"Errors: 400 for a bad request, 401 for an invalid token, 404 when the conversation,
contact, workflow, or campaign ID isn’t found, 413 when the upload is too large, and 415 for
an unsupported file type.
Record an inbound message
Section titled “Record an inbound message”If a contact messages you on a channel your account doesn’t handle natively, such as a custom chat widget or a third-party number, use this endpoint to log what they sent so it appears in their conversation history like any other message.
type and conversationProviderId are required, plus either conversationId or contactId.
If you send contactId without conversationId, the message is added to that contact’s
conversation. conversationProviderId is the ID of the custom conversation provider the
message belongs to. Set type to the channel and supply the matching fields.
direction defaults to outbound, so set it to inbound to record a message the contact sent.
| Body field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | One of SMS, Email, WhatsApp, GMB, IG, FB, Custom, WebChat, Live_Chat, or Call. |
conversationProviderId |
string | Yes | ID of the conversation provider. |
conversationId |
string | One of the two | The conversation to add the message to. |
contactId |
string | One of the two | The contact the message is from. |
direction |
string | No | inbound or outbound. Default outbound. |
message |
string | No | Message text. |
date |
string | No | Date and time of the message, as an ISO 8601 date-time. |
attachments |
array | No | Attachment URLs. |
altId |
string | No | The external provider’s ID for the message. |
html |
string | No | HTML body, for email. |
subject |
string | No | Subject line, for email. |
emailFrom, emailTo |
string | No | Sender and recipient addresses. They come from the contact record and can’t be changed here. |
emailCc, emailBcc |
array | No | Email addresses to copy or blind copy. |
emailMessageId |
string | No | The email message ID to thread this email under, for a reply to a specific email. |
call |
object | No | For Call messages: to and from phone numbers, and status (pending, completed, answered, busy, no-answer, failed, canceled, or voicemail). |
curl -X POST https://services.smbcrm.com/conversations/messages/inbound \ -H "Authorization: Bearer <token>" \ -H "Version: v3" \ -H "Content-Type: application/json" \ -d '{ "type": "SMS", "contactId": "<contact_id>", "conversationId": "<conversation_id>", "conversationProviderId": "<conversation_provider_id>", "direction": "inbound", "message": "Sounds good, see you then!" }'The response has success, conversationId, messageId, and message. It also returns
contactId, dateAdded, and, for emails, emailMessageId.
{ "success": true, "conversationId": "<conversation_id>", "messageId": "<message_id>", "message": "success", "contactId": "<contact_id>", "dateAdded": "2026-07-08T15:04:00.000Z"}