> ## Documentation Index
> Fetch the complete documentation index at: https://docs.replyful.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a ticket

> POST /v1/tickets — open a ticket from your own chatbot with one message or a whole chat

Creates a ticket on one of your chat channels, handled by AI (your bot) until you [hand it over](https://docs.replyful.com/api-reference/tickets/handover.md). Send one message to start a live chat, or the whole transcript at once. See [Bring your own chatbot](https://docs.replyful.com/api-reference/own-chatbot.md) for the full flow.

```http theme={null}
POST /v1/tickets
```

## Authentication

A **Read & write** live key. See [Authentication](https://docs.replyful.com/api-reference/authentication.md).

## Body

**channelId** `string` *required*

One of your chat channels. It decides which teams see the ticket and where a handover goes.

**messages** `array` *required*

1 to 100 messages, oldest first. The first must have `role: "user"`.

**properties**

**role** `string` *required*

`user` (the customer) or `assistant` (your bot).

**content** `string` *required*

Plain text, up to 20,000 characters.

**createdAt** `string`

ISO 8601 time the message was written, in UTC (`2026-11-10T09:14:02Z`) or with an offset (`2026-11-10T10:14:02+01:00`). Defaults to now. Cannot be in the future. Send it on every message or on none, oldest first.

**externalId** `string`

Your own ID for the chat, up to 128 characters. Unique per organization: a second ticket with the same ID returns `409 ticket_external_id_taken`.

**requester** `object`

Who the customer is. Leave it out for an anonymous visitor.

**properties**

**externalId** `string`

Your user ID. Finds or creates the one contact for that user.

**firstName** `string`

First name.

**lastName** `string`

Last name.

**email** `object`

**properties**

**email** `string` *required*

The email address.

**verified** `boolean`

`true` when you have checked that the person owns the address, so personal data may be sent there. Defaults to `false`. Must be `true` when you also send `externalId`: leave the email out for a user whose address you have not checked.

**id** `string`

A Replyful contact ID (`requester.id` from an earlier ticket) to reuse that contact. Send it on its own.

Unknown fields are refused with `400 unknown_field`.

## Response

`201` with the [`Ticket` object](https://docs.replyful.com/api-reference/tickets/list#the-ticket-object), including its `conversations`. Post further messages to the conversation with `role: "primary"`.

## Example

```bash curl theme={null}
curl https://api.replyful.com/v1/tickets \
  -H "Authorization: Bearer rfl_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9c7f1ad1-6c4f-4d4d-bfde-3b1e6e1c8f5a" \
  -d '{
    "channelId": "k3Jd82La",
    "externalId": "chat-8812",
    "requester": {
      "externalId": "user_42",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "email": { "email": "ada@example.com", "verified": true }
    },
    "messages": [
      { "role": "user", "content": "My invoice is wrong" },
      { "role": "assistant", "content": "Let me check that for you." }
    ]
  }'
```

```ts Node.js theme={null}
const res = await fetch("https://api.replyful.com/v1/tickets", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.REPLYFUL_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    channelId: "k3Jd82La",
    externalId: "chat-8812",
    requester: { externalId: "user_42", firstName: "Ada", lastName: "Lovelace" },
    messages: [{ role: "user", content: "My invoice is wrong" }],
  }),
});
const ticket = await res.json();
const conversationId = ticket.conversations[0].id;
```

```json Response theme={null}
{
  "id": "ti_8kNwq2Tt7yGkX9wM4bDfP",
  "object": "ticket",
  "externalId": "chat-8812",
  "title": "My invoice is wrong",
  "status": "open",
  "handledBy": "ai",
  "requester": {
    "id": "con_9dKmT3xWq6bYrN8cJfA2eH4gp",
    "externalId": "user_42",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "name": "Ada Lovelace",
    "email": { "email": "ada@example.com", "verified": true },
    "phone": null
  },
  "conversations": [
    { "id": "conv_4tGh7Kq2Lm9Zx1B", "object": "conversation", "role": "primary", "status": "waiting_for_user" }
  ]
}
```

The response is shortened here; it carries every field of the ticket object.

## Errors

| Status | Code | Meaning |
| - | - | - |
| `403` | `write_access_required` | The key is read-only. |
| `403` | `test_mode_write_not_supported` | Test keys cannot write yet. |
| `409` | `ticket_external_id_taken` | A ticket with this `externalId` exists. The message names it. |
| `422` | `invalid_channel` | `channelId` is not one of your chat channels. |
| `422` | `requester_not_found` | `requester.id` matches no contact. |
| `422` | `invalid_body` | A field failed validation. `param` names it. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.