SLA Policies
Define response and resolution targets with business hours, read a ticket's live SLA clock, and list what is breaching.
Overview
An SLA policy promises two things about a ticket: how fast someone will respond, and how fast it will be resolved. A policy may be scoped to one priority, or left un-scoped as the catch-all.
Deadlines are computed against the ticket's server-managed lifecycle stamps, so a policy cannot be gamed by editing a timestamp.
Breach is computed on read, never stored. That means the numbers are always current, and it has one consequence for pagination — see the note on /breaches.
Authentication
Authorization: Bearer cm_your_api_key| Operation | Scope |
|---|---|
| List policies, ticket status, breaches | sla:read |
| Create, update, delete a policy | sla:write |
Business hours
A policy with business_hours only burns its clock during those hours. Omit it and the clock runs continuously.
| Field | Type | Default | Constraints |
|---|---|---|---|
tz | string | null | Your tenant's timezone | At most 64 characters |
days | integer[] | [1,2,3,4,5] | 1–7 entries, values 1–7 where Monday is 1 and Sunday is 7. Stored sorted and de-duplicated |
start | string | "09:00" | HH:MM, 24-hour |
end | string | "18:00" | HH:MM, must be later than start |
{ "tz": "Asia/Kolkata", "days": [1,2,3,4,5,6], "start": "10:00", "end": "19:00" }The policy object
{
"id": "d1c2…",
"tenant_id": "a0b1…",
"name": "Urgent — 15 min first response",
"priority": "urgent",
"first_response_minutes": 15,
"resolution_minutes": 240,
"business_hours": null,
"is_active": true,
"created_at": "2026-08-10T08:00:00Z",
"updated_at": "2026-08-10T08:00:00Z"
}GET /api/v1/support/sla/policies
Returns an array of policy objects, newest first.
| Parameter | Type | Constraints |
|---|---|---|
priority | string | At most 16 characters, matched lowercase |
is_active | boolean | |
limit | integer | 1 <= limit <= 200, default 50 |
offset | integer | 0 <= offset <= 100000, default 0 |
curl "https://api.callmissed.com/api/v1/support/sla/policies?is_active=true" \
-H "Authorization: Bearer cm_your_api_key"POST /api/v1/support/sla/policies
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | Yes | 1–255 characters, not blank, unique per tenant |
priority | string | No | At most 16 characters, must start with a letter. Omit for a catch-all policy |
first_response_minutes | integer | No | 1 <= n <= 100000 |
resolution_minutes | integer | No | 1 <= n <= 100000 |
business_hours | object | No | See above |
is_active | boolean | No | Default true |
At least one of first_response_minutes / resolution_minutes must be set — a policy that promises nothing is rejected with 422 set at least one of first_response_minutes / resolution_minutes.
curl -X POST https://api.callmissed.com/api/v1/support/sla/policies \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Urgent — 15 min first response",
"priority": "urgent",
"first_response_minutes": 15,
"resolution_minutes": 240
}'A duplicate name returns 409 An SLA policy named '…' already exists.
PATCH /api/v1/support/sla/policies/{policy_id}
All fields optional; only the fields you send are changed. Sending business_hours: null removes business hours (the clock then runs continuously); sending priority: null turns the policy into a catch-all. The "must promise something" rule is re-checked against the merged result, so you cannot clear both minute fields in two steps. Returns the updated policy.
curl -X PATCH https://api.callmissed.com/api/v1/support/sla/policies/{policy_id} \
-H "Authorization: Bearer cm_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"first_response_minutes": 30,
"business_hours": { "tz": "Asia/Kolkata", "days": [1,2,3,4,5], "start": "09:00", "end": "18:00" }
}'404 SLA policy not found; 409 on a name another policy already uses.
DELETE /api/v1/support/sla/policies/{policy_id}
Returns 204. 404 SLA policy not found.
curl -X DELETE https://api.callmissed.com/api/v1/support/sla/policies/{policy_id} \
-H "Authorization: Bearer cm_your_api_key"GET /api/v1/support/sla/status/{ticket_id}
The live clock for one ticket.
curl https://api.callmissed.com/api/v1/support/sla/status/{ticket_id} \
-H "Authorization: Bearer cm_your_api_key"{
"ticket_id": "e5d4…",
"policy_id": "d1c2…",
"policy_name": "Urgent — 15 min first response",
"first_response_due_at": "2026-08-17T06:25:00Z",
"first_response_breached": true,
"resolution_due_at": "2026-08-17T10:10:00Z",
"resolution_breached": false,
"minutes_remaining": 84
}| Field | Type | Notes |
|---|---|---|
policy_id | UUID | null | null when no policy matched this ticket |
minutes_remaining | integer | null | Wall-clock minutes to the nearest still-running deadline. Negative when overdue. null when both clocks have stopped or no policy matched |
first_response_breached / resolution_breached | boolean | A stopped clock is judged against its stamp: a ticket first answered after the deadline stays breached for good, and one answered in time never becomes breached later |
Policy selection: only active policies (is_active: true) are considered. A policy whose priority matches the ticket's priority (case-insensitive) wins; otherwise the oldest catch-all policy applies; otherwise nothing does and every due date is null.
404 Ticket not found.
GET /api/v1/support/sla/status
The same answer as /status/{ticket_id}, for up to 200 tickets in one call — what a ticket list uses to show every row's clock.
| Parameter | Type | Required | Constraints |
|---|---|---|---|
ticket_id | UUID (repeatable) | Yes | 1–200 ids; duplicates are ignored |
curl "https://api.callmissed.com/api/v1/support/sla/status?ticket_id={id_1}&ticket_id={id_2}" \
-H "Authorization: Bearer cm_your_api_key"Returns an array of status objects. Ids that are not tickets in your tenant are left out of the answer rather than reported as errors.
GET /api/v1/support/sla/breaches
Tickets currently past a deadline, oldest first.
| Parameter | Type | Constraints |
|---|---|---|
priority | string | At most 16 characters, matched lowercase |
include_resolved | boolean | Default false, which skips tickets that have a resolved_at stamp |
limit | integer | 1 <= limit <= 200, default 50 |
offset | integer | 0 <= offset <= 100000, default 0 |
curl "https://api.callmissed.com/api/v1/support/sla/breaches?priority=urgent&limit=200" \
-H "Authorization: Bearer cm_your_api_key"[
{
"ticket_id": "e5d4…",
"subject": "Refund not received",
"status": "open",
"priority": "urgent",
"created_at": "2026-08-17T06:10:00Z",
"sla": {
"ticket_id": "e5d4…",
"policy_id": "d1c2…",
"policy_name": "Urgent — 15 min first response",
"first_response_due_at": "2026-08-17T06:25:00Z",
"first_response_breached": true,
"resolution_due_at": "2026-08-17T10:10:00Z",
"resolution_breached": false,
"minutes_remaining": -32
}
}
]sla has the same shape as the /status/{ticket_id} response.
Pagination behaves differently here. Because breach is computed rather than stored,
limitandoffsetpage over the tickets scanned (oldest first), not the breaches returned. A page can come back shorter thanlimit, or empty, and still have more behind it — the response does not tell you how many tickets were scanned. To sweep everything, advanceoffsetbylimiteach call untiloffsetpasses your ticket count, rather than stopping at the first short or empty page.
Errors
| Status | When |
|---|---|
403 | Key is missing sla:read / sla:write |
404 | Policy or ticket not in your tenant |
409 | Duplicate policy name |
422 | Policy promises nothing, blank name, minutes outside 1..100000, a priority that is not a short identifier, or end not later than start |
Nothing on this page consumes credits.
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.
Macros, Tags & Routing
Canned replies with placeholders and side-effect actions, a tag vocabulary, and a first-match-wins routing engine with a dry-run evaluator.