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 · uuid

    Unique identifier for the conversation.

  • titlestring · nullable

    The first 80 characters of the first message. Null until a message is sent.

  • context_typestring · nullable

    The type of record the conversation is about. Null for a general conversation.

    contactorganizationthreaddeal
  • context_idstring · uuid · nullable

    The id of that record.

  • statusstring

    An archived conversation 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 messages

    Present only when you retrieve a conversation: its latest 50 messages, oldest first.

The conversation object
{
  "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 · uuid

    Unique identifier for the message.

  • rolestring

    Who wrote it. tool messages are intermediate steps; clients normally hide them.

    userassistanttool
  • contentstring · nullable

    The 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 in refs.

  • statusstring

    User messages are complete when created. An assistant reply starts pending, is streaming while it is written, and ends complete or failed.

    pendingstreamingcompletefailed
  • error_codestring · nullable

    Why a reply failed. usage_limit_reached means the account has reached its assistant usage limit; every other failure is reply_failed. After a failed reply, the conversation accepts the next message.

    usage_limit_reachedreply_failed
  • intermediateboolean

    True for an assistant message that handed off to tools before the final answer; clients normally hide these, as they do tool messages. False for every other message, including the final answer.

  • feedback_ratingstring · nullable

    The user’s rating of an assistant reply, set with Rate a message.

    updown
  • feedback_reasonstring · nullable

    The comment that came with the rating.

  • support_idstring · uuid · nullable

    An identifier for this reply. Include it when you report a problem with a reply.

  • refsarray of objects

    Records the reply cites, each as a type in snake case, such as thread or calendar_event, an id, and a label: the record’s name as the user sees it, such as a contact’s name, a thread’s subject, or a task’s assignment. label is null when the user can no longer see the record.

  • blocksarray of objects

    The reply in parts, in order. Each has a type.

    Show 6 block typesHide block types
    • text

      A passage of text in text.

    • proposal

      A change the assistant proposed or made. Carries proposal_id and the proposal’s current action_type, status, record_type and record_id (null for a proposal that creates a record), applied_record_type and applied_record_id (the record it changed or created, once applied), display, actions, and resolution_reason, read fresh each time, so the card reflects whether it has been accepted. A tag.apply or tag.remove block also has tag_id, which is null while a proposed new tag doesn’t exist yet. If the user can no longer see the proposal, only proposal_id is present and status is unavailable.

    • cards

      A list of records to show as cards: a card_type of contact, organization, thread, deal, or task, an optional title, and their ids.

    • table

      A table with an optional title, columns, and rows. Each column has a key, a label, and an optional kind: money, date, number, or text. 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 has cells keyed by column, with null for an empty cell, and may have a ref to the record it describes, with a snake-case type and an id.

    • tool_use

      On an intermediate assistant message: a tool the assistant called, with the call’s id, the tool’s name, and its input object.

    • tool_result

      On a tool message: the output of the tool_use block whose id matches tool_use_id, as a string in content, and is_error, true when the call failed.

  • created_atstring · date-time
The message object
{
  "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
    }
  ]
}
get/conversations Read key

#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

  • pageintegerdefault 1
  • per_pageintegerdefault 50

    Values above 100 are treated as 100.

Returns

  • collectionobject

    A page of conversations, without their messages.

    Show 7 child attributesHide child attributes
    • recordsarray of conversations
    • pageinteger
    • per_pageinteger
    • total_resultsinteger
    • pagesinteger
    • sortstring

      Always last_message_at.

    • orderstring

      Always desc.

Errors

  • 400invalid_requestpage or per_page is less than 1 or not a number.
get/conversations
curl https://api.carom.io/conversations \
  -H "Authorization: Bearer $CAROM_API_KEY"
Response200
{
  "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",
        …
      }
    ]
  }
}
post/conversations Write key

#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_typestring

    Required with context_id. Any other value, including a capitalized one such as Deal, returns 400 invalid_request.

    contactorganizationthreaddeal
  • context_idstring · uuid

    A 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

  • 400invalid_requestcontext_type isn’t one of the listed values.
  • 422invalid_contextOnly one of context_type and context_id was given, or the record isn’t visible to your key.
post/conversations
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"
  }'
Response201
{
  "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"
  }
}
get/conversations/{id} Read key

#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 · uuidrequired

    The conversation’s id.

Returns

The conversation object with messages, wrapped in conversation.

Errors

  • 404record_not_foundYour key’s user has no conversation with that id.
get/conversations/{id}
curl https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86 \
  -H "Authorization: Bearer $CAROM_API_KEY"
Response200
{
  "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/conversations/{id} Write key

#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

  • 404record_not_foundYour key’s user has no conversation with that id.
delete/conversations/{id}
curl -X DELETE https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86 \
  -H "Authorization: Bearer $CAROM_API_KEY"
Response204
No content
get/conversations/{id}/messages Read key

#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 · uuidrequired

    The conversation’s id.

Query parameters

  • beforestring · uuid

    A message id. Only messages older than that message are returned. An id that isn’t in this conversation returns no messages.

  • limitintegerdefault 50

    How many messages to return, between 1 and 100. Larger values are treated as 100.

Returns

  • messagesarray of messages

    The newest limit messages older than before, in the order they were written.

Errors

  • 400invalid_requestlimit is less than 1 or not a number.
  • 404record_not_foundYour key’s user has no conversation with that id.
get/conversations/{id}/messages
curl "https://api.carom.io/conversations/e1a3c5e7-9b1d-4f3a-8b5c-7d9f1b3d5f86/messages?limit=2" \
  -H "Authorization: Bearer $CAROM_API_KEY"
Response200
{
  "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": []
    }
  ]
}
post/conversations/{id}/messages Write key

#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 · uuidrequired

    The conversation’s id.

Request body application/json

  • contentstringrequired

    What the user says, up to 4,000 characters. Leading and trailing whitespace is removed.

Returns

  • messagemessage

    The user’s message as stored.

  • assistant_message_idstring · uuid

    The id of the assistant message that will hold the reply. It exists immediately, with status pending.

Errors

  • 400invalid_requestcontent is missing or longer than 4,000 characters.
  • 403feature_disabledThe assistant is turned off for the account, or the user has opted out of it. Check capabilities.ask_carom.
  • 404record_not_foundYour key’s user has no conversation with that id.
  • 409conversation_archivedThe conversation has been deleted.
  • 409turn_in_progressThe assistant is still answering the previous message.
  • 422invalid_messagecontent is empty once whitespace is removed.
  • 429rate_limitedThe user has sent too many messages recently. Wait the number of seconds in the Retry-After header.
post/conversations/{id}/messages
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?" }'
Response202
{
  "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"
}
post/conversations/{id}/messages/{message_id}/feedback Write key

#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 · uuidrequired

    The conversation’s id.

  • message_idstring · uuidrequired

    An assistant message in that conversation whose status is complete.

Request body application/json

  • ratingstringrequired
    updown
  • reasonstring · nullable

    What 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

  • 400invalid_requestrating isn’t up or down, or reason is too long.
  • 404record_not_foundThe conversation isn’t your key’s user’s, or the message isn’t a finished assistant reply in it.
post/conversations/{id}/messages/{message_id}/feedback
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" }'
Response200
{
  "message": {
    "id": "6d8f0a2c-4e6a-4c8e-a0c2-5e7a9c1e3a39",
    "role": "assistant",
    "status": "complete",
    "feedback_rating": "up",
    "feedback_reason": null,
    …
  }
}