> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scaling.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Escalation policies API

> Create an escalation policy, add layers with acknowledgment timeouts, attach it to incidents, reorder layers, and gate layers with follow-the-sun working hours conditions using the scaling.cloud API.

An escalation policy defines the chain of responders that scaling.cloud notifies when an incident is opened. Each layer targets an on-call schedule (or a specific set of users) and has an acknowledgment timeout. If no one on that layer acknowledges the incident within the timeout window, scaling.cloud moves to the next layer.

<Tip>
  Prefer to work in the dashboard? See [Configure Escalation](/guides/configure-escalation-policy) and [Follow-the-sun escalation](/guides/follow-the-sun-escalation) for the UI walkthroughs.
</Tip>

## Prerequisites

* At least one on-call schedule created. See the [On-call schedules API](/developers/oncall-api) if you haven't created one yet.
* A scaling.cloud API key.

<Steps>
  <Step title="Create an escalation policy">
    Send a `POST` request to `/api/escalation/policies` with a name and optional description.

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/escalation/policies \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Platform Default",
        "description": "Default escalation for platform incidents"
      }'
    ```

    The response returns the new policy:

    ```json theme={null}
    {
      "data": {
        "id": "pol_01jqm5x3k8vz9e2f4g7h",
        "orgId": "org_01jqm4ab2c3de5fg6hij",
        "name": "Platform Default",
        "description": "Default escalation for platform incidents",
        "createdBy": "user_01jqm3xy4z5abc6de7fg",
        "createdAt": "2026-04-07T10:00:00.000Z",
        "updatedAt": "2026-04-07T10:00:00.000Z"
      }
    }
    ```

    The policy has no layers yet—add them in the next step.
  </Step>

  <Step title="Add escalation layers">
    Each layer specifies a `position`, an `ackTimeoutMinutes`, and a `target`. The target is either an on-call schedule or a direct list of users.

    **Add layer 1** — targets your primary on-call schedule with a 5-minute acknowledgment timeout:

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/escalation/policies/pol_01jqm5x3k8vz9e2f4g7h/layers \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "position": 1,
        "ackTimeoutMinutes": 5,
        "target": {
          "type": "schedule",
          "scheduleId": "sched_01jqm5x3k8vz9e2f4g7h"
        }
      }'
    ```

    Response:

    ```json theme={null}
    {
      "data": {
        "id": "layer_01jqm6a2b3cd4ef5gh6i",
        "policyId": "pol_01jqm5x3k8vz9e2f4g7h",
        "orgId": "org_01jqm4ab2c3de5fg6hij",
        "position": 1,
        "ackTimeoutMinutes": 5,
        "target": {
          "type": "schedule",
          "scheduleId": "sched_01jqm5x3k8vz9e2f4g7h"
        },
        "createdAt": "2026-04-07T10:05:00.000Z",
        "updatedAt": "2026-04-07T10:05:00.000Z"
      }
    }
    ```

    **Add layer 2** — targets a secondary schedule with a 10-minute timeout. This layer fires if no one on the first schedule acknowledges within 5 minutes:

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/escalation/policies/pol_01jqm5x3k8vz9e2f4g7h/layers \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "position": 2,
        "ackTimeoutMinutes": 10,
        "target": {
          "type": "schedule",
          "scheduleId": "sched_01jqm7c3d4ef5gh6ij7k"
        }
      }'
    ```

    Response:

    ```json theme={null}
    {
      "data": {
        "id": "layer_01jqm7b3c4de5fg6hi7j",
        "policyId": "pol_01jqm5x3k8vz9e2f4g7h",
        "orgId": "org_01jqm4ab2c3de5fg6hij",
        "position": 2,
        "ackTimeoutMinutes": 10,
        "target": {
          "type": "schedule",
          "scheduleId": "sched_01jqm7c3d4ef5gh6ij7k"
        },
        "createdAt": "2026-04-07T10:06:00.000Z",
        "updatedAt": "2026-04-07T10:06:00.000Z"
      }
    }
    ```

    <Note>
      If no one acknowledges an incident within a layer's `ackTimeoutMinutes`, scaling.cloud automatically notifies the next layer in the policy. If all layers are exhausted without an acknowledgment, scaling.cloud stops escalating but the incident remains open. Make sure your final layer has a long enough timeout or targets a reliable fallback—such as a manager or secondary team.
    </Note>

    You can also target specific users directly instead of a schedule by using `"type": "users"` with a `userIds` array:

    ```json theme={null}
    {
      "position": 3,
      "ackTimeoutMinutes": 15,
      "target": {
        "type": "users",
        "userIds": ["user_01jqm3xy4z5abc6de7fg"]
      }
    }
    ```
  </Step>

  <Step title="Attach the policy to an incident">
    Pass the policy ID in the `escalationPolicyId` field when creating an incident. scaling.cloud immediately begins notifying the first layer's on-call responders.

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/incidents \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "title": "Payment service degraded",
        "severity": "critical",
        "description": "Checkout success rate dropped to 60% in the last 10 minutes",
        "escalationPolicyId": "pol_01jqm5x3k8vz9e2f4g7h"
      }'
    ```

    The response confirms the policy is attached:

    ```json theme={null}
    {
      "data": {
        "id": "inc_01jqm9d5e6fg7hi8jk9l",
        "orgId": "org_01jqm4ab2c3de5fg6hij",
        "title": "Payment service degraded",
        "description": "Checkout success rate dropped to 60% in the last 10 minutes",
        "severity": "critical",
        "status": "investigating",
        "escalationPolicyId": "pol_01jqm5x3k8vz9e2f4g7h",
        "createdBy": "user_01jqm3xy4z5abc6de7fg",
        "createdAt": "2026-04-07T10:10:00.000Z",
        "updatedAt": "2026-04-07T10:10:00.000Z"
      }
    }
    ```
  </Step>

  <Step title="Reorder layers">
    If you need to change the escalation order, send a `PUT` request to `/api/escalation/policies/{policyId}/layers/order` with the full ordered list of layer IDs. The array order determines the new `position` values.

    ```bash theme={null}
    curl -X PUT https://api.scaling.cloud/v1/escalation/policies/pol_01jqm5x3k8vz9e2f4g7h/layers/order \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "layerIds": [
          "layer_01jqm7b3c4de5fg6hi7j",
          "layer_01jqm6a2b3cd4ef5gh6i"
        ]
      }'
    ```

    The response returns all layers with updated positions:

    ```json theme={null}
    {
      "data": [
        {
          "id": "layer_01jqm7b3c4de5fg6hi7j",
          "policyId": "pol_01jqm5x3k8vz9e2f4g7h",
          "orgId": "org_01jqm4ab2c3de5fg6hij",
          "position": 1,
          "ackTimeoutMinutes": 10,
          "target": {
            "type": "schedule",
            "scheduleId": "sched_01jqm7c3d4ef5gh6ij7k"
          },
          "createdAt": "2026-04-07T10:06:00.000Z",
          "updatedAt": "2026-04-07T10:20:00.000Z"
        },
        {
          "id": "layer_01jqm6a2b3cd4ef5gh6i",
          "policyId": "pol_01jqm5x3k8vz9e2f4g7h",
          "orgId": "org_01jqm4ab2c3de5fg6hij",
          "position": 2,
          "ackTimeoutMinutes": 5,
          "target": {
            "type": "schedule",
            "scheduleId": "sched_01jqm5x3k8vz9e2f4g7h"
          },
          "createdAt": "2026-04-07T10:05:00.000Z",
          "updatedAt": "2026-04-07T10:20:00.000Z"
        }
      ]
    }
    ```

    You must include every layer ID in the request—the API will reject a `layerIds` array that doesn't match the full set of layers on the policy.
  </Step>
</Steps>

## Follow-the-sun: gate layers with working hours

Follow-the-sun escalation pages the team that is awake. You gate a policy's layers with [working hours conditions](/concepts/working-hours#layer-conditions-follow-the-sun-escalation) so a daytime incident reaches your UK rotation during UK hours and your US rotation otherwise — without maintaining parallel policies. For the concepts, see [Working hours](/concepts/working-hours); for the dashboard walkthrough, see [Follow-the-sun escalation](/guides/follow-the-sun-escalation).

<Steps>
  <Step title="Create the working hours sets">
    Create one set per region. Each set carries its own timezone — it is never inferred from a schedule — and one or more weekly windows.

    Create **UK office hours**:

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/working-hours \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "UK office hours",
        "timezone": "Europe/London",
        "windows": [
          { "days": [1, 2, 3, 4, 5], "start": "09:00", "end": "17:00" }
        ]
      }'
    ```

    Create **US office hours**:

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/working-hours \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "US office hours",
        "timezone": "America/New_York",
        "windows": [
          { "days": [1, 2, 3, 4, 5], "start": "08:00", "end": "18:00" }
        ]
      }'
    ```

    `days` are ISO weekday numbers — `1` = Monday through `7` = Sunday — and `start`/`end` are 24-hour `HH:MM` times in the set's timezone. Note each `id` in the response; you'll reference it from a layer condition next.
  </Step>

  <Step title="Gate the layers with conditions">
    Order two conditional layers on your escalation policy: page the UK rotation `during` UK hours, then the US rotation `during` US hours. A layer's condition has the shape `{ workingHoursId, when }`, where `when` is `during` or `outside`.

    Add **layer 1** — the UK rotation, gated `during` UK office hours:

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/escalation/policies/POLICY_ID/layers \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "position": 1,
        "ackTimeoutMinutes": 5,
        "target": { "type": "schedule", "scheduleId": "UK_SCHEDULE_ID" },
        "condition": { "workingHoursId": "UK_WORKING_HOURS_ID", "when": "during" }
      }'
    ```

    Add **layer 2** — the US rotation, gated `during` US office hours:

    ```bash theme={null}
    curl -X POST https://api.scaling.cloud/v1/escalation/policies/POLICY_ID/layers \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "position": 2,
        "ackTimeoutMinutes": 10,
        "target": { "type": "schedule", "scheduleId": "US_SCHEDULE_ID" },
        "condition": { "workingHoursId": "US_WORKING_HOURS_ID", "when": "during" }
      }'
    ```

    Use `outside` when you want a layer to participate only outside a set's windows — for example a night-shift layer gated `outside` UK office hours. Omit `condition` to make a layer unconditional.
  </Step>
</Steps>

<Note>
  Two rules make this safe. **Skip, never wait:** a layer whose condition does not hold is skipped and escalation proceeds to the next layer immediately. **Backstop:** if *every* layer's condition fails at an evaluation instant, the last layer is paged unconditionally, so a page never silently dies to misconfiguration. Conditions are evaluated at the instant escalation reaches the layer, never at incident creation. A good pattern is to make your **final** layer unconditional.
</Note>

## Next steps

<Card title="Manage working hours as code" href="/integrations/terraform#scaling_working_hours" icon="terminal">
  The `scaling_working_hours` Terraform resource and the step `condition` block.
</Card>
