Create Conversation
/v1/conversationsCreates a new conversation on behalf of a customer contact via an external integration. The first message is always sent as the customer (contact) — agent-initiated messages are not supported. Message type is always plain text.
Either contact_id or contact_name must be provided:
- If
contact_idis provided, the contact must already exist and belong to the authenticated workspace. - If
contact_idis omitted, a new contact is created usingcontact_name.
Requires chat:write scope.
Authorization
BearerAuth In: header
Request Body
application/json
Conversation creation details. Provide either contact_id (existing contact) or contact_name (creates a new contact).
TypeScript Definitions
Use the request body type in TypeScript.
Request body for creating a conversation from an external source.
Provide either contact_id to use an existing contact, or contact_name to create a new one.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/conversations" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "a24f8ef9-2da9-47e0-9be5-0dbeb40a8d54", "contact_id": "550e8400-e29b-41d4-a716-446655440000", "message": "Hi, I need help with my order #12345." }'{ "request_id": "550e8400-e29b-41d4-a716-446655440003"}Create Attachment Upload URL POST
Requests a presigned URL for uploading an attachment file. The client uploads the file directly to the returned `upload_url` via HTTP PUT, then references the returned `attachment_id` when sending an attachment message. The upload URL is single-use, expires after ~5 minutes, and pins both `Content-Type` and `Content-Length` to the values declared in this request. Requires `chat:write` scope.
Send Conversation Message POST
Sends a message to a conversation. The message is enqueued for asynchronous delivery. Supports plain text messages and forms. Requires `chat:write` scope. ### Closing the conversation in the same call Set `transition_status_to` to close the conversation right after this message is delivered — for a goodbye note, or a "this chat has ended" notice. Use this rather than sending the message and then calling `PUT /v1/conversations/{id}/status` yourself: as two separate requests they can overtake each other. | Value | Meaning | |---|---| | `RESOLVED` | Closed, but the contact reopens it by sending another message. | | `FINALIZED` | Permanently closed. You will need a new conversation next time. | | `OPEN` | Not accepted. Sending any message into a resolved conversation reopens it already. | | Rule | Behavior | |---|---| | Ordering | The message always lands first — the conversation is only closed once the message is persisted, so the customer never sees a closed chat missing your last message. | | Response | Still `202`, immediately. The close happens moments later in the background; the response acknowledges the message, not the close. | | End state | The one you asked for, whatever the conversation was doing beforehand. If it was already closed, the message reopens it and it closes again — agents may see a brief reopen in the history. If it was already in the requested state, nothing changes. | | `FINALIZED` from `OPEN` | Resolved first, then finalized — matching `PUT /v1/conversations/{id}/status`. | | Enablement | Requires the capability to be switched on for your workspace. Until it is, the field is **silently ignored**: the message is sent normally and returns `202`, but the conversation stays open. | | Already `FINALIZED` | A finalized conversation rejects new messages, so this cannot add a farewell to a chat that is already permanently closed. Create a new conversation instead. | | Invalid value | `400`, and no message is sent. (While the capability is off nothing is validated, so the same request returns `202` instead.) | **Best-effort, and a failure is silent.** The message is never affected — it is sent, stored and delivered regardless. But if the close itself fails (transient backend problem, or someone changed the status in the meantime) the conversation keeps its previous status and nothing tells you: the `202` was only ever about the message, and there is no webhook for a close that did not happen. If the final status matters, read it back with `GET /v1/conversations/{id}` and fall back to `PUT /v1/conversations/{id}/status`.