Read your tickets with the API

With the same workspace API key, your backend can read a ticket’s current status and its customer-visible conversation, or list all of one customer’s tickets. The read-back is exactly what the customer sees — agents’ private notes and AI summaries are never included.

Read one ticket

Send a GET to /api/tickets/{id-or-ref}. You can use either the Mongo id or the short, human-facing ref (case-insensitive), so the value you stored from the create response works directly:

curl https://{slug}.essential.support/api/tickets/A7K2M9Q \
  -H "Authorization: Bearer es_live_…"

The response is the ticket plus its customer-visible thread. Each message carries its kind (customer, agent_reply, and instant/auto AI replies), a plain-text and HTML body, and the author’s display name:

{
  "ticket": {
    "id": "66b2f0a1c3d4e5f6a7b8c9d0",
    "number": 1042,
    "ref": "A7K2M9Q",
    "subject": "Invoice charged twice",
    "status": "open",
    "priority": "high",
    "source": "api",
    "createdAt": "2026-08-09T14:03:00.000Z",
    "lastMessageAt": "2026-08-09T15:20:00.000Z",
    "customer": { "id": "66b2efaa1122334455667788", "email": "dana@customer.com", "externalId": "u_9", "name": "Dana Lee" }
  },
  "messages": [
    { "id": "66b2f0a1…", "kind": "customer", "body": { "text": "I was billed twice this month.", "html": "<p>I was billed twice this month.</p>" },
      "author": { "type": "endUser", "displayName": "Dana Lee", "avatarUrl": null }, "attachments": [], "createdAt": "2026-08-09T14:03:00.000Z" },
    { "id": "66b2f5c2…", "kind": "agent_reply", "body": { "text": "We've refunded the duplicate charge.", "html": "<p>We've refunded the duplicate charge.</p>" },
      "author": { "type": "agent", "displayName": "Sam", "avatarUrl": null }, "attachments": [], "createdAt": "2026-08-09T15:20:00.000Z" }
  ]
}

An unknown id or ref returns 404 with Ticket not found.

List a customer’s tickets

Send a GET to /api/tickets. A keyed caller must name exactly one customer, so you must pass email or externalId (a request with neither returns 400):

curl "https://{slug}.essential.support/api/tickets?externalId=u_9&status=open&limit=25" \
  -H "Authorization: Bearer es_live_…"

Tickets come back newest-activity first (by lastMessageAt), with a total for paging:

{
  "total": 3,
  "tickets": [
    { "id": "66b2f0a1…", "ref": "A7K2M9Q", "subject": "Invoice charged twice", "status": "open", "priority": "high", "source": "api",
      "customer": { "email": "dana@customer.com", "externalId": "u_9", "name": "Dana Lee" } }
  ]
}

An unknown customer is not an error — you get { "total": 0, "tickets": [] }. The list is never an upsert: it only finds an existing customer.

Discover your ticket types

Call GET /api/ticket-types with your key to list your workspace’s types (id and name), so you can set type by id when creating a ticket.

Pairs with webhooks

The recommended pattern is thin webhook payloads (ids and state) that trigger a read-back: your app receives ticket.agent_replied, then calls GET /api/tickets/{ref} for the detail. See Set up webhooks.