Skip to content

Escalation API

These endpoints manage what Settings → Escalation shows: the teams that share a case queue, and the escalation policies that decide where each kind of case starts and goes. To put one case in a team’s queue, or filter the case list by queue, see the Alerts and cases API.

Who can call them. Signed-in people, not API keys; an API key gets 401 sign in required. Sign in with curl as described in Alerts and cases API: before you start, then add -b ~/.sp-cookies to every request below.

To You need
Read teams and policies Any one of cases.view, settings.view, settings.manage
Create, change or delete a team settings.manage
Create, change or delete a policy settings.manage, and by default a second person with settings.approve

Every change is written to the audit log.

Endpoint Does Success
GET /api/v1/teams Teams and their members 200
POST /api/v1/teams Create a team 201
PUT /api/v1/teams/{id} Rename a team and set its members 200
DELETE /api/v1/teams/{id} Delete a team 204
GET /api/v1/escalation-policies Policies, in the order they are tried 200
POST /api/v1/escalation-policies Create a policy 201, or 202 when sent for approval
PUT /api/v1/escalation-policies/{id} Change a policy 200, or 202
DELETE /api/v1/escalation-policies/{id} Delete a policy 204, or 202

Errors use Problem Details.

Status Meaning Examples
401 Not signed in, or called with an API key sign in required
403 Missing permission missing permission settings.manage
404 No such team or policy in your workspace not found
409 A name is taken, or the team is in use conflict: a team with this name already exists; conflict: the escalation policy "Politically exposed persons" sends cases to this team; change the policy first
422 The request is invalid name must be 1 to 80 characters; a level's time must be between 15 minutes and 90 days; level 2 names a team that doesn't exist; condition: …
428, 202 The change needs a second person’s approval (policies only). See When approval is required
Field Type Meaning
id UUID The team
name string 1 to 80 characters, unique in the workspace
members array Leads first, then by name. Each: user_id, name (read-only display name) and lead (boolean)
active_cases integer Cases in the team’s queue that aren’t closed

lead is a label: it changes nothing about permissions or notifications.

Every team, in name order.

Terminal window
curl -s -b ~/.sp-cookies "$SP/api/v1/teams"
[
{
"id": "0192f3d0-aaaa-7000-8000-000000000001",
"name": "PEP desk",
"members": [
{ "user_id": "0192f3a0-bbbb-7000-8000-000000000002", "name": "Efua Owusu", "lead": true },
{ "user_id": "0192f3a0-bbbb-7000-8000-000000000003", "name": "Kofi Boateng", "lead": false }
],
"active_cases": 3
}
]

Illustrative data.

POST /api/v1/teams and PUT /api/v1/teams/{id}

Section titled “POST /api/v1/teams and PUT /api/v1/teams/{id}”
Field Required Values
name Yes 1 to 80 characters
members Yes Up to 500 entries of {"user_id": …, "lead": true | false}. May be empty. A user listed twice is kept once

PUT replaces the name and the whole member list: send everyone who should remain. Every member must be an active user of your workspace; otherwise 422 a member is not an active person in this workspace and nothing changes. Both return the team object.

Terminal window
curl -s -b ~/.sp-cookies -X POST "$SP/api/v1/teams" \
-H 'Content-Type: application/json' \
-d '{"name":"PEP desk","members":[{"user_id":"0192f3a0-bbbb-7000-8000-000000000002","lead":true}]}'

Deletes the team. Its cases leave the queue (team_id is cleared) and stay with their assignees. 409 while any escalation policy, in use or off, names the team for a level. Returns 204.

Field Type Meaning
id UUID The policy. Read-only
name string 1 to 80 characters, unique in the workspace
priority integer 1 to 1000. The order: when several policies fit a case, the lowest number wins; ties go in name order
condition string Which cases the policy takes, up to 1,000 characters. Empty: every case. See The condition
start_level integer 1 to 3. The level a matching case opens at. Above 1, the case opens escalated and unassigned
levels array Up to three entries, one per level; see below. Levels you leave out have no team and use the workspace’s deadline
auto_escalate boolean Escalate the case one level when a deadline passes
four_eyes_close boolean Closing a case that follows this policy needs a second approver, whatever its priority
active boolean false: the policy fits no new case. Cases already following it carry on
active_cases integer Cases following the policy that aren’t closed. Read-only
updated_at time Read-only

Each entry of levels:

Field Type Meaning
level integer 1, 2 or 3. Each level at most once
team_id UUID The team whose queue cases join at this level. Optional
team_name string Read-only
sla_minutes integer How long a case has at this level, in minutes: 15 to 129600 (90 days). 0 or absent uses the workspace’s case_sla_hours for the case’s priority

An entry with neither a team nor a time is dropped when the policy is stored.

A CEL expression that evaluates to true or false, over:

Name Type Values
kind string aml, fraud, sanctions, other
priority string low, medium, high, critical
score double The case’s opening score
subject_type string customer, account, counterparty
designations list of strings The subject’s designations in force, for example "pep" in designations
risk_rating string unrated, low, medium, high, unacceptable, or empty
segment string The customer’s segment code, or empty
kyc_level int The customer’s KYC tier; 0 when unknown

An expression that doesn’t compile, or doesn’t yield a boolean, is refused with 422. The condition is evaluated once, when a case is opened. If it fails to evaluate for a case, the policy doesn’t fit that case.

Every policy, in the order they are tried.

[
{
"id": "0192f3d1-cccc-7000-8000-000000000001",
"name": "Politically exposed persons",
"priority": 10,
"condition": "\"pep\" in designations || \"pep_rca\" in designations",
"start_level": 2,
"levels": [
{ "level": 2, "team_id": "0192f3d0-aaaa-7000-8000-000000000001", "team_name": "PEP desk", "sla_minutes": 1440 }
],
"auto_escalate": true,
"four_eyes_close": true,
"active": true,
"active_cases": 3,
"updated_at": "2026-10-12T09:30:00Z"
}
]

Illustrative data.

POST /api/v1/escalation-policies and PUT /api/v1/escalation-policies/{id}

Section titled “POST /api/v1/escalation-policies and PUT /api/v1/escalation-policies/{id}”

Send name, priority, condition, levels, start_level, auto_escalate, four_eyes_close and active. PUT replaces the whole policy. Both return the policy object when applied directly.

Terminal window
curl -s -b ~/.sp-cookies -X POST "$SP/api/v1/escalation-policies" \
-H 'Content-Type: application/json' \
-H 'X-Change-Reason: PEP cases go to the PEP desk at level 2 (procedure AML-07)' \
-d '{
"name": "Politically exposed persons",
"priority": 10,
"condition": "\"pep\" in designations || \"pep_rca\" in designations",
"start_level": 2,
"levels": [{ "level": 2, "team_id": "0192f3d0-aaaa-7000-8000-000000000001", "sla_minutes": 1440 }],
"auto_escalate": true,
"four_eyes_close": true,
"active": true
}'

Cases already open keep their level, team and deadline when a policy changes: a changed team or time applies from their next escalation. four_eyes_close and auto_escalate are read from the policy as it is now, so those two apply to open cases at once.

Deletes a policy that no open case follows. Returns 204 when applied directly.

While open cases follow the policy the request is refused with 409: N open cases follow this policy; switch it off so it takes no new case, and delete it when they are closed. Set active to false with PUT instead; active_cases in the list tells you when it can be deleted.

Policy changes fall under the action type cases.escalation_policies (Escalation policies), which requires a second person by default. While it does, the three writing endpoints answer as every gated endpoint does:

  1. Without a reason: 428 Reason required. The policy is checked first, so an invalid one gets its 422 or 409 here.
  2. With an X-Change-Reason header: 202 Accepted, with the pending request in change_request. Nothing has changed yet. Someone else with settings.approve approves it, and the policy is created, changed or deleted then.

A second change to the same policy while one is pending is refused with 409. See Change requests API for the full exchange, polling and expiry.

Team endpoints are never gated.