# Open a ticket

> Opens a ticket from outside the chat — a form on your own site, or an inbox you triage by hand.

Source: https://www.hey.support/docs/api/tickets/create-ticket

---

<ApiEndpoint method="POST" path="/tickets" />

It goes through the same path a ticket from the chat takes, so the number is allocated the same way and your team hears about it once. A customer who already has an open ticket gets their message added to it rather than a second number; the response is then 200 with that ticket. Set `notify_customer: false` for a backfill, where a receipt for something said last week is confusing.

## Request body

<TypeTable
  type={{
  "chatbot_id": { type: "string", description: "Which agent this belongs to.", required: true },
  "email": { type: "string (email)", description: "Where the team replies. Required — a ticket nobody can answer is not a ticket.", required: true },
  "body": { type: "string", description: "What the problem is, in the customer’s words.", required: true },
  "subject": { type: "string", description: "", required: false },
  "name": { type: "string", description: "", required: false },
  "phone": { type: "string", description: "", required: false },
  "category_id": { type: "integer", description: "One of the agent’s categories. Omit and the triage pass picks one.", required: false },
  "answers": { type: "object", description: "Answers to the category’s own fields, keyed by field id.", required: false },
  "notify_customer": { type: "boolean", description: "", required: false }
}}
/>

## Response `200`

<TypeTable
  type={{
  "id": { type: "string (uuid)", description: "Ticket UUID (not sqid-encoded)", required: true },
  "chatbot_id": { type: "string", description: "", required: true },
  "conversation_id": { type: "string", description: "The chat it came out of, when it came out of one.", required: false },
  "customer_id": { type: "integer", description: "The workspace-level customer record — the same person across every agent in the workspace.", required: false },
  "number": { type: "integer", description: "What the customer quotes. Unique per workspace, not per agent.", required: true },
  "status": { type: "\"new\" | \"open\" | \"waiting\" | \"solved\" | \"closed\"", description: "", required: true },
  "priority": { type: "\"low\" | \"normal\" | \"high\" | \"urgent\"", description: "", required: true },
  "sentiment": { type: "\"angry\" | \"frustrated\" | \"neutral\" | \"happy\"", description: "How the customer sounds, from the triage pass. Null until it has run.", required: false },
  "category_id": { type: "integer", description: "", required: false },
  "category_name": { type: "string", description: "Snapshotted, so a deleted category leaves the ticket readable.", required: false },
  "subject": { type: "string", description: "", required: false },
  "summary": { type: "string", description: "One line from the triage pass — not the customer’s own words.", required: false },
  "name": { type: "string", description: "", required: false },
  "email": { type: "string", description: "", required: false },
  "phone": { type: "string", description: "", required: false },
  "answers": { type: "object", description: "The category’s own fields, keyed by field id.", required: false },
  "source": { type: "\"agent\" | \"card\" | \"manual\" | \"handoff\" | \"api\" | \"channel\"", description: "", required: true },
  "channel": { type: "string", description: "", required: false },
  "language": { type: "string", description: "", required: false },
  "assignee_user_id": { type: "integer", description: "", required: false },
  "rating": { type: "integer", description: "", required: false },
  "first_reply_at": { type: "string (date-time)", description: "", required: false },
  "last_customer_message_at": { type: "string (date-time)", description: "", required: false },
  "last_agent_message_at": { type: "string (date-time)", description: "", required: false },
  "promised_at": { type: "string (date-time)", description: "When the team promised to reply (the receipt's \"Expect a reply Monday morning\"). Null when no time was promised.", required: false },
  "solved_at": { type: "string (date-time)", description: "", required: false },
  "closed_at": { type: "string (date-time)", description: "", required: false },
  "reopened_count": { type: "integer", description: "How many times a customer reply brought it back.", required: false },
  "created_at": { type: "string (date-time)", description: "", required: true },
  "updated_at": { type: "string (date-time)", description: "", required: true }
}}
/>

### Errors

* `400` — Invalid body, or a category that no longer exists
* `401` — Missing or invalid API key
* `402` — Tickets are not on this workspace’s plan
* `404` — Chatbot not found
* `409` — This agent is not taking new tickets — switch Tickets on for it in Capabilities
* `429` — Rate limit exceeded

Every error shares [one envelope](/docs/api/errors).

## Example

```bash
curl -X POST \
  https://www.hey.support/api/v1/tickets \
  -H 'Authorization: Bearer hs_live_…' \
  -H 'Content-Type: application/json' \
  -d '{"chatbot_id":"<chatbot_id>","email":"<email>","body":"<body>"}'
```
