Conversations
A conversation is a chat between one user and Carom’s assistant, which answers questions about that user’s mail, contacts, deals, and tasks. Conversations are private: only the user who started one can read it or post to it, so your key sees only your key’s user’s conversations.
The assistant answers in the background. Sending a message returns at once with the id of the assistant message that will hold the reply; list the messages until that one’s status is complete or failed. Every change the assistant makes to a record is recorded as a proposal, which appears in the reply. The user’s assistant settings decide, for each kind of change, whether it is applied at once (status auto_applied, and undoable) or waits for the user to accept it. Sending an email always waits.
#The conversation object
A conversation can be about a particular record, given by context_type and context_id. The assistant uses that record as context.
Attributes
-
idstring · uuidUnique identifier for the conversation.
-
titlestring · nullableThe first 80 characters of the first message. Null until a message is sent.
-
context_typestring · nullableThe type of record the conversation is about. Null for a general conversation.
contactorganizationthreaddeal -
context_idstring · uuid · nullableThe id of that record.
-
statusstringAn
archivedconversation has been deleted. It can still be read but no longer takes messages.activearchived -
last_message_atstring · date-time · nullable -
created_atstring · date-time -
updated_atstring · date-time -
messagesarray of messagesPresent only when you retrieve a conversation: its latest 50 messages, oldest first.
{
"id": "e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86",
"title": "What does Priya need from us before the renewal?",
"context_type": "deal",
"context_id": "8f2b6d4a-1c9e-4a7b-b3d5-0e6f8a2c4d17",
"status": "active",
"last_message_at": "2026-09-18T16:30:58.158Z",
"created_at": "2026-09-18T16:30:41.836Z",
"updated_at": "2026-09-18T16:30:58.158Z"
}
#The message object
One message in a conversation. content is the plain text; blocks is the same reply broken into parts a client can render, including proposal cards and lists of records. Intermediate steps also carry the assistant’s tool calls and their results as blocks.
Attributes
-
idstring · uuidUnique identifier for the message.
-
rolestringWho wrote it.
toolmessages are intermediate steps; clients normally hide them.userassistanttool -
contentstring · nullableThe message text. Null on an assistant message that hasn’t finished. A record the reply cites appears inline as
[ref:<type>:<id>], such as[ref:calendar_event:0b2d4f6a-8c0e-4a2c-b4e6-1d3f5a7c9e02], with the type in snake case; the same records are listed inrefs. -
statusstringUser messages are
completewhen created. An assistant reply startspending, isstreamingwhile it is written, and endscompleteorfailed.pendingstreamingcompletefailed -
error_codestring · nullableWhy a reply failed.
usage_limit_reachedmeans the account has reached its assistant usage limit; every other failure isreply_failed. After a failed reply, the conversation accepts the next message.usage_limit_reachedreply_failed -
intermediatebooleanTrue for an assistant message that handed off to tools before the final answer; clients normally hide these, as they do
toolmessages. False for every other message, including the final answer. -
feedback_ratingstring · nullableThe user’s rating of an assistant reply, set with Rate a message.
updown -
feedback_reasonstring · nullableThe comment that came with the rating.
-
support_idstring · uuid · nullableAn identifier for this reply. Include it when you report a problem with a reply.
-
refsarray of objectsRecords the reply cites, each as a
typein snake case, such asthreadorcalendar_event, anid, and alabel: the record’s name as the user sees it, such as a contact’s name, a thread’s subject, or a task’s assignment.labelis null when the user can no longer see the record. -
blocksarray of objectsThe reply in parts, in order. Each has a
type.Show 6 block typesHide block types
-
textA passage of text in
text. -
proposalA change the assistant proposed or made. Carries
proposal_idand the proposal’s currentaction_type,status,record_typeandrecord_id(null for a proposal that creates a record),applied_record_typeandapplied_record_id(the record it changed or created, once applied),display,actions, andresolution_reason, read fresh each time, so the card reflects whether it has been accepted. Atag.applyortag.removeblock also hastag_id, which is null while a proposed new tag doesn’t exist yet. If the user can no longer see the proposal, onlyproposal_idis present andstatusisunavailable. -
cardsA list of records to show as cards: a
card_typeofcontact,organization,thread,deal, ortask, an optionaltitle, and theirids. -
tableA table with an optional
title,columns, androws. Each column has akey, alabel, and an optionalkind:money,date,number, ortext. Money and number cells are numbers, or a string when the value couldn’t be read as a number; date cells are strings. Each row hascellskeyed by column, withnullfor an empty cell, and may have arefto the record it describes, with a snake-casetypeand anid. -
tool_useOn an
intermediateassistant message: a tool the assistant called, with the call’sid, the tool’sname, and itsinputobject. -
tool_resultOn a
toolmessage: the output of thetool_useblock whoseidmatchestool_use_id, as a string incontent, andis_error, true when the call failed.
-
-
created_atstring · date-time
{
"id": "6d8f0a2c-4e6a-4c8e-a0c2-5e7a9c1e3a39",
"role": "assistant",
"content": "Priya asked for the 2027 renewal numbers by Friday, and nobody has replied yet. I've proposed a task to send them.",
"status": "complete",
"error_code": null,
"intermediate": false,
"feedback_rating": null,
"feedback_reason": null,
"support_id": "0c2e4a6c-8e0a-4c2e-9a4c-6e8a0c2e4a71",
"refs": [
{
"type": "thread",
"id": "7a9c1e3f-5b7d-4f9a-a1c3-8e0b2d4f6a55",
"label": "Re: 2027 renewal terms"
}
],
"created_at": "2026-09-18T16:30:42.811Z",
"blocks": [
{
"type": "text",
"text": "Priya asked for the 2027 renewal numbers by Friday, and nobody has replied yet. I've proposed a task to send them."
},
{
"type": "proposal",
"proposal_id": "9c1e3a5c-7e9a-4c1e-b3a5-0e2a4c6e8a52",
"action_type": "task.create",
"status": "pending",
"record_type": null,
"record_id": null,
"applied_record_type": null,
"applied_record_id": null,
"display": {
"assignment": "Send Priya the 2027 renewal numbers",
"description": null,
"due_on": "2026-09-18",
"calendar_event_name": null,
"thread_subject": null,
"assigned_users": [
{ "id": "9c41d2e8-3f6a-4b7c-8d1e-5a2f7b9c0d3e", "name": "Dana Whitfield" }
],
"linked_records": [
{ "type": "deal", "id": "8f2b6d4a-1c9e-4a7b-b3d5-0e6f8a2c4d17", "name": "Wildgrove portfolio renewal" }
]
},
"actions": ["accept", "reject"],
"resolution_reason": null
}
]
}
#List conversations
Returns your key’s user’s active conversations, most recent message first. Deleted conversations are not listed.
This list is paged by page number, 50 per page by default, rather than by cursor. See Page-number pagination.
Query parameters
-
pageintegerdefault1 -
per_pageintegerdefault50Values above 100 are treated as 100.
Returns
-
collectionobjectA page of conversations, without their messages.
Show 7 child attributesHide child attributes
-
recordsarray of conversations pageintegerper_pageintegertotal_resultsintegerpagesintegersortstringAlways
last_message_at.orderstringAlways
desc.
-
Errors
- 400
invalid_requestpageorper_pageis less than 1 or not a number.
curl https://api.carom.io/conversations \
-H "Authorization: Bearer $CAROM_API_KEY"
{
"collection": {
"sort": "last_message_at",
"order": "desc",
"per_page": 50,
"page": 1,
"total_results": 2,
"pages": 1,
"records": [
{
"id": "e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86",
"title": "What does Priya need from us before the renewal?",
"context_type": "deal",
"context_id": "8f2b6d4a-1c9e-4a7b-b3d5-0e6f8a2c4d17",
"status": "active",
"last_message_at": "2026-09-18T16:30:58.158Z",
…
},
{
"id": "3f5b7d9f-1b3d-4f5b-8d9f-2b4d6f8b0d19",
"title": "Which deals are closing this month?",
"context_type": null,
"context_id": null,
"status": "active",
"last_message_at": "2026-09-15T10:12:44.473Z",
…
}
]
}
}
#Create a conversation
Starts a conversation, empty until you send a message. Send no body for a general conversation, or a context to make it about one record.
If your key’s user already has an active conversation about that record, it is returned instead of a new one, so calling this again for the same record resumes the same chat. A request with no context always creates a new conversation.
Request body application/json
-
context_typestringRequired with
context_id. Any other value, including a capitalized one such asDeal, returns400 invalid_request.contactorganizationthreaddeal -
context_idstring · uuidA record of that type visible to your key. Required with
context_type.
Returns
The conversation object, wrapped in conversation, with status 201 whether the conversation is new or existing.
Errors
- 400
invalid_requestcontext_typeisn’t one of the listed values. - 422
invalid_contextOnly one ofcontext_typeandcontext_idwas given, or the record isn’t visible to your key.
curl https://api.carom.io/conversations \
-H "Authorization: Bearer $CAROM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"context_type": "deal",
"context_id": "8f2b6d4a-1c9e-4a7b-b3d5-0e6f8a2c4d17"
}'
{
"conversation": {
"id": "e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86",
"title": null,
"context_type": "deal",
"context_id": "8f2b6d4a-1c9e-4a7b-b3d5-0e6f8a2c4d17",
"status": "active",
"last_message_at": null,
"created_at": "2026-09-18T16:30:41.836Z",
"updated_at": "2026-09-18T16:30:41.836Z"
}
}
#Retrieve a conversation
Returns a conversation with its latest 50 messages, oldest first. Works for deleted conversations too. For earlier messages, use List messages.
Path parameters
-
idstring · uuidrequiredThe conversation’s id.
Returns
The conversation object with messages, wrapped in conversation.
Errors
- 404
record_not_foundYour key’s user has no conversation with that id.
curl https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86 \
-H "Authorization: Bearer $CAROM_API_KEY"
{
"conversation": {
"id": "e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86",
"title": "What does Priya need from us before the renewal?",
"context_type": "deal",
"context_id": "8f2b6d4a-1c9e-4a7b-b3d5-0e6f8a2c4d17",
"status": "active",
…
"messages": [
{
"id": "3b5d7f9a-1c3e-4a5c-9e7a-0c2e4a6c8e14",
"role": "user",
"content": "What does Priya need from us before the renewal?",
"status": "complete",
…
},
{
"id": "6d8f0a2c-4e6a-4c8e-a0c2-5e7a9c1e3a39",
"role": "assistant",
"content": "Priya asked for the 2027 renewal numbers by Friday, and nobody has replied yet. I've proposed a task to send them.",
"status": "complete",
…
}
]
}
}
#Delete a conversation
Archives the conversation. It disappears from List conversations and stops accepting messages, but you can still retrieve it and its messages by id. Deleting a conversation that is already archived succeeds and changes nothing.
Path parameters
-
idstring · uuidrequired
Returns
An empty response with status 204.
Errors
- 404
record_not_foundYour key’s user has no conversation with that id.
curl -X DELETE https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86 \
-H "Authorization: Bearer $CAROM_API_KEY"
No content
#List messages
Returns the latest messages in a conversation, oldest first. Poll it after sending a message to pick up the reply.
To read further back, pass the id of the oldest message you have as before. This list does not use the cursor model described under Cursor pagination; an empty messages array means you have reached the start.
Path parameters
-
idstring · uuidrequiredThe conversation’s id.
Query parameters
-
beforestring · uuidA message id. Only messages older than that message are returned. An id that isn’t in this conversation returns no messages.
-
limitintegerdefault50How many messages to return, between 1 and 100. Larger values are treated as 100.
Returns
-
messagesarray of messagesThe newest
limitmessages older thanbefore, in the order they were written.
Errors
- 400
invalid_requestlimitis less than 1 or not a number. - 404
record_not_foundYour key’s user has no conversation with that id.
curl "https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86/messages?limit=2" \
-H "Authorization: Bearer $CAROM_API_KEY"
{
"messages": [
{
"id": "3b5d7f9a-1c3e-4a5c-9e7a-0c2e4a6c8e14",
"role": "user",
"content": "What does Priya need from us before the renewal?",
"status": "complete",
"error_code": null,
"intermediate": false,
"feedback_rating": null,
"feedback_reason": null,
"support_id": null,
"refs": [],
"created_at": "2026-09-18T16:30:41.836Z",
"blocks": [
{
"type": "text",
"text": "What does Priya need from us before the renewal?"
}
]
},
{
"id": "6d8f0a2c-4e6a-4c8e-a0c2-5e7a9c1e3a39",
"role": "assistant",
"content": null,
"status": "streaming",
"error_code": null,
…
"blocks": []
}
]
}
#Send a message
Adds the user’s message to the conversation and starts the assistant’s reply. The response has status 202 and comes back before the reply is written: it contains the stored message and assistant_message_id, the id of the reply. List messages until that message is complete or failed.
A conversation answers one message at a time. Sending another while a reply is still being written returns turn_in_progress. The assistant answers only when the account has it turned on and the user hasn’t opted out of it. capabilities.ask_carom on Retrieve your key’s user is true when both hold.
Path parameters
-
idstring · uuidrequiredThe conversation’s id.
Request body application/json
-
contentstringrequiredWhat the user says, up to 4,000 characters. Leading and trailing whitespace is removed.
Returns
-
messagemessageThe user’s message as stored.
-
assistant_message_idstring · uuidThe id of the assistant message that will hold the reply. It exists immediately, with status
pending.
Errors
- 400
invalid_requestcontentis missing or longer than 4,000 characters. - 403
feature_disabledThe assistant is turned off for the account, or the user has opted out of it. Checkcapabilities.ask_carom. - 404
record_not_foundYour key’s user has no conversation with that id. - 409
conversation_archivedThe conversation has been deleted. - 409
turn_in_progressThe assistant is still answering the previous message. - 422
invalid_messagecontentis empty once whitespace is removed. - 429
rate_limitedThe user has sent too many messages recently. Wait the number of seconds in theRetry-Afterheader.
curl https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86/messages \
-H "Authorization: Bearer $CAROM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "content": "What does Priya need from us before the renewal?" }'
{
"message": {
"id": "3b5d7f9a-1c3e-4a5c-9e7a-0c2e4a6c8e14",
"role": "user",
"content": "What does Priya need from us before the renewal?",
"status": "complete",
"error_code": null,
"intermediate": false,
"feedback_rating": null,
"feedback_reason": null,
"support_id": null,
"refs": [],
"created_at": "2026-09-18T16:30:41.836Z",
"blocks": [
{
"type": "text",
"text": "What does Priya need from us before the renewal?"
}
]
},
"assistant_message_id": "6d8f0a2c-4e6a-4c8e-a0c2-5e7a9c1e3a39"
}
#Rate a message
Records a thumbs-up or thumbs-down on a finished assistant reply, with an optional comment. Rating the same message again replaces the earlier rating.
Path parameters
-
idstring · uuidrequiredThe conversation’s id.
-
message_idstring · uuidrequiredAn assistant message in that conversation whose status is
complete.
Request body application/json
-
ratingstringrequiredupdown -
reasonstring · nullableWhat was good or wrong about the reply, up to 1,000 characters. A blank value is stored as
null.
Returns
The message object with its new feedback_rating and feedback_reason, wrapped in message.
Errors
- 400
invalid_requestratingisn’tupordown, orreasonis too long. - 404
record_not_foundThe conversation isn’t your key’s user’s, or the message isn’t a finished assistant reply in it.
curl https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86/messages/6d8f0a2c-4e6a-4c8e-a0c2-5e7a9c1e3a39/feedback \
-H "Authorization: Bearer $CAROM_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "rating": "up" }'
{
"message": {
"id": "6d8f0a2c-4e6a-4c8e-a0c2-5e7a9c1e3a39",
"role": "assistant",
"status": "complete",
"feedback_rating": "up",
"feedback_reason": null,
…
}
}