Support Tickets
Create, filter, assign and move support tickets through their lifecycle, with server-managed response and resolution stamps.
Overview
A ticket is one unit of support work. It can stand alone, or hang off a conversation and a contact so an agent sees the thread that produced it.
The lifecycle timestamps — first_responded_at, resolved_at, closed_at, reopened_count — are server-managed. You never send them; you change status and the API stamps the rest. That is what makes the SLA endpoints and CSAT reporting trustworthy.
Authentication
Authorization: Bearer cm_your_api_key| Operation | Scope |
|---|---|
| List, get, list comments | support_tickets:read |
| Create, update, assign, status, delete, add comment | support_tickets:write |
Enumerations
| Field | Values |
|---|---|
status | open, pending, waiting_on_customer, resolved, closed |
priority | low, normal, high, urgent |
resolved and closed are the terminal statuses; the other three are active.
The ticket object
{
"id": "e5d4…",
"tenant_id": "a0b1…",
"conversation_id": "c0ff…",
"contact_id": "4411…",
"subject": "Refund not received",
"description": "Customer says the refund has not landed after 7 days.",
"status": "open",
"priority": "high",
"assignee_user_id": null,
"tags": ["billing", "refund"],
"first_responded_at": null,
"resolved_at": null,
"closed_at": null,
"reopened_count": 0,
"created_at": "2026-08-17T06:10:00Z",
"updated_at": "2026-08-17T06:10:00Z"
}| Field | Type | Notes |
|---|---|---|
assignee_user_id | UUID | null | null means the ticket is in the unassigned queue |
tags | string[] | null | Trimmed, blanks dropped, de-duplicated, order preserved |
first_responded_at | datetime | null | Stamped once, the first time the ticket leaves open. Never re-stamped |
resolved_at | datetime | null | Stamped on the move to resolved |
closed_at | datetime | null | Stamped on the move to closed |
reopened_count | integer | Incremented each time a terminal ticket returns to an active status |
GET /api/v1/support/tickets
Returns an array of ticket objects, newest first.
| Parameter | Type | Required | Constraints |
|---|---|---|---|
status | string | No | One of the five statuses |
priority | string | No | One of the four priorities |
assignee_user_id | UUID | No | One agent's queue |
contact_id | UUID | No | |
conversation_id | UUID | No | |
unassigned | boolean | No | true = no assignee, false = has one. Cannot be combined with assignee_user_id |
q | string | No | At most 255 characters. Case-insensitive substring on subject |
limit | integer | No | 1 <= limit <= 200, default 50 |
offset | integer | No | 0 <= offset <= 100000, default 0 |
curl "https://api.callmissed.com/api/v1/support/tickets?status=open&unassigned=true&limit=50" \
-H "Authorization: Bearer cm_your_api_key"Sending both unassigned=true and assignee_user_id returns 422 unassigned=true cannot be combined with assignee_user_id — the two contradict each other, so the API refuses rather than silently picking one.
POST /api/v1/support/tickets
| Field | Type | Required | Constraints |
|---|---|---|---|
subject | string | Yes | 1–255 characters, not blank |
description | string | No | At most 20,000 characters |
conversation_id | UUID | No | Must exist in your tenant |
contact_id | UUID | No | Must exist in your tenant |
assignee_user_id | UUID | No | Must be a user in your tenant |
status | string | No | Default open |
priority | string | No | Default normal |
tags | string[] | No | At most 20 tags, each at most 64 characters |
curl -X POST https://api.callmissed.com/api/v1/support/tickets \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"subject": "Refund not received",
"description": "Customer says the refund has not landed after 7 days.",
"conversation_id": "c0ffee00-1111-2222-3333-444455556666",
"priority": "high",
"tags": ["billing", "refund"]
}'Returns 201. Creating a ticket directly as resolved or closed stamps the matching timestamp immediately.
Your active routing rules run on create, first match wins: a matching rule fills in assignee_user_id only when you sent none, sets priority only when you sent none, and adds its tags to yours. Values you send always win.
404 Conversation not found / Contact not found / Assignee not found when a linked id is not in your tenant.
GET /api/v1/support/tickets/{ticket_id}
One ticket. 404 Ticket not found.
curl https://api.callmissed.com/api/v1/support/tickets/{ticket_id} \
-H "Authorization: Bearer cm_your_api_key"PATCH /api/v1/support/tickets/{ticket_id}
Accepts the same editable fields as create. The lifecycle timestamps and reopened_count are not accepted — a status change here runs the same transition rules as the dedicated status endpoint.
Every field is optional; only the fields you send are changed. Linked ids (conversation_id, contact_id, assignee_user_id) are checked against your tenant exactly as on create. Returns the updated ticket.
curl -X PATCH https://api.callmissed.com/api/v1/support/tickets/{ticket_id} \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "priority": "urgent", "tags": ["billing", "escalated"] }'subject sent as blank or null returns 422 subject must not be blank. An explicit null for status or priority is ignored rather than written.
POST /api/v1/support/tickets/{ticket_id}/assign
| Field | Type | Required | Notes |
|---|---|---|---|
assignee_user_id | UUID | null | No | null (or omitted) unassigns and returns the ticket to the queue |
curl -X POST https://api.callmissed.com/api/v1/support/tickets/{ticket_id}/assign \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "assignee_user_id": "b1f2c3d4-1111-2222-3333-444455556666" }'Returns the updated ticket. 404 Assignee not found when the user is not in your tenant.
POST /api/v1/support/tickets/{ticket_id}/status
| Field | Type | Required | Constraints |
|---|---|---|---|
status | string | Yes | One of the five statuses |
curl -X POST https://api.callmissed.com/api/v1/support/tickets/{ticket_id}/status \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "status": "resolved" }'Returns the updated ticket. Idempotent. Sending the status the ticket already has returns it untouched — no re-stamp, no reopened_count increment. Safe to retry.
Transition rules
| Move | Effect |
|---|---|
| Same status | No-op |
→ resolved | Stamps resolved_at if unset, clears closed_at |
→ closed | Stamps closed_at, leaves resolved_at alone |
| Terminal → active | Clears both stamps, reopened_count += 1 |
Leaving open for the first time | Stamps first_responded_at once |
A logged reply (see comments) also stamps first_responded_at if nothing has yet.
GET /api/v1/support/tickets/{ticket_id}/comments
The ticket's thread — internal notes and logged replies — oldest first.
| Parameter | Type | Required | Constraints |
|---|---|---|---|
limit | integer | No | 1–200, default 100 |
offset | integer | No | 0–100000, default 0 |
curl https://api.callmissed.com/api/v1/support/tickets/{ticket_id}/comments \
-H "Authorization: Bearer cm_your_api_key"[
{
"id": "9a1c…",
"ticket_id": "e5d4…",
"author_user_id": "b1f2…",
"body": "Refund re-issued, reference RF-2210.",
"is_internal": false,
"created_at": "2026-08-17T07:02:00Z"
}
]author_user_id is null for entries added with an API key. 404 Ticket not found.
POST /api/v1/support/tickets/{ticket_id}/comments
| Field | Type | Required | Constraints |
|---|---|---|---|
body | string | Yes | 1–20000 characters, not blank |
is_internal | boolean | No | Default true. true is a team-only note; false records a reply the customer was given |
A comment is recorded on the ticket only — nothing is sent to the customer. The first entry with is_internal: false stamps first_responded_at if it is still unset; internal notes never do.
curl -X POST https://api.callmissed.com/api/v1/support/tickets/{ticket_id}/comments \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "body": "Refund re-issued, reference RF-2210.", "is_internal": false }'Returns 201 with the comment. 404 Ticket not found, 422 for a blank body.
DELETE /api/v1/support/tickets/{ticket_id}
Returns 204. 404 Ticket not found.
curl -X DELETE https://api.callmissed.com/api/v1/support/tickets/{ticket_id} \
-H "Authorization: Bearer cm_your_api_key"Errors
| Status | When |
|---|---|
403 | Key is missing support_tickets:read / support_tickets:write |
404 | Ticket, conversation, contact or assignee is not in your tenant |
422 | Unknown status/priority, blank subject, more than 20 tags or a tag over 64 characters, or unassigned combined with assignee_user_id |
Nothing on this page consumes credits.
Invoices & Payments
Issue numbered invoices with tax, send them to customers, record payments, collect online through your connected payment gateway, and track what is overdue.
Handoffs
The cross-channel queue of conversations waiting for a human: list and count them, assign an owner, resolve them back to the bot, and call a voice customer back.