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

# Escalate if no response

> Automatically move an access request to a second set of reviewers when the first reviewers don't respond in time.

Access requests stall when the assigned reviewer is asleep, on vacation, or buried in their inbox. Escalation adds a timer to a stage of your approval workflow: if nobody reviews the request within the time you set, Opal automatically escalates it to an additional set of reviewers.

## Prerequisites

* You must be an Opal Admin, or an **Admin** of the resource or group you're configuring.
* The stage you add escalation to must use **Any** logic. Escalation can't be added to a stage that requires **All** reviewers to approve. See [Approval flow](/docs/configure-reviewers#approval-flow).

## How escalation works

When you add escalation to a stage, Opal adds an **Escalation stage** directly after it. The escalation stage contains the original stage's reviewers *plus* the owners and users you escalate to.

* If any original reviewer responds before the timer expires, the request proceeds normally and the escalation stage is satisfied at the same time. Nothing extra happens.
* If nobody responds before the timer expires, the request advances to the escalation stage, and the escalated reviewers are notified.
* The original reviewers stay on the escalation stage, so they can still approve after escalation. Escalation widens the pool of people who can act; it doesn't take the request away from anyone.

The escalation stage is derived from the stage above it. To change its reviewers, edit the original stage—reviewers you add there are copied forward automatically.

<Note>
  Escalation only widens the pool of reviewers. It never grants access on its own—a request that nobody acts on still expires the way it normally would.
</Note>

## Configure escalation

1. Go to the resource or group's **Edit** page and expand **Request Configuration**.

2. In the **Approval Flow** section, find the stage you want to add a timer to and select **Escalate if no response**.

   <img src="https://mintcdn.com/opalsecurity/lSA2RHHsTN1R9x6u/images/docs/escalation-add-button.png?fit=max&auto=format&n=lSA2RHHsTN1R9x6u&q=85&s=8ed5e9d7233d8e13b50533c9403ee38a" alt="Approval Flow section of a request configuration. Stage 1 has an owner assigned and a row of buttons below it: Requester's manager, Entity Admin, Secure with Paladin, and Escalate if no response." width="1850" height="2082" data-path="images/docs/escalation-add-button.png" />

3. Under **Escalate after**, choose how long to wait before escalating:

   | Option | Wait time |
   | - | - |
   | 30 minutes | 30 minutes |
   | 1 hour | 1 hour |
   | 2 hours | 2 hours |
   | 4 hours | 4 hours |
   | 8 hours | 8 hours |
   | 24 hours | 24 hours |
   | Custom | Any value from 1 to 1440 minutes (24 hours) |

4. Select the **owners** and **users** to escalate to. You can pick any combination, but you must select at least one.

   <img src="https://mintcdn.com/opalsecurity/lSA2RHHsTN1R9x6u/images/docs/escalation-configure-modal.png?fit=max&auto=format&n=lSA2RHHsTN1R9x6u&q=85&s=048428fe956f3e71b53daa8ba60d6a56" alt="The &#x22;Escalate if no one responds&#x22; dialog showing an Escalate after dropdown set to Custom, a Custom delay field, and two selects for owners and users to escalate to." width="3444" height="1970" data-path="images/docs/escalation-configure-modal.png" />

5. Select **Add escalation**. The new **Escalation stage** appears below the stage you configured.

6. Save the request configuration.

To remove escalation later, delete the **Escalation stage**. The original stage keeps its reviewers.

## What escalation looks like on a request

On a pending request, the **Reviewers** section labels both halves of the pair. The original stage is tagged with the wait time you configured, and the stage below it is tagged **ESCALATION**.

<img src="https://mintcdn.com/opalsecurity/lSA2RHHsTN1R9x6u/images/docs/escalation-request-stages.png?fit=max&auto=format&n=lSA2RHHsTN1R9x6u&q=85&s=08d4957ba3aaa9bab90146648c632826" alt="A pending request's Reviewers section. Stage 1 is tagged &#x22;escalates if not reviewed in 240 minutes&#x22; and Stage 2 is tagged &#x22;escalation&#x22;, with the same owner and reviewers listed on both stages. An Escalation Timer service user appears as an individual reviewer on Stage 1 with a status of Approved." width="3010" height="1985" data-path="images/docs/escalation-request-stages.png" />

Opal carries out the timeout with an **Escalation Timer** service user, which Opal provisions for you and adds as a read-only reviewer on the timed stage. When the window expires, the timer clears that stage so the request advances, and comments on the request—`No reviewer responded in time - escalating.`—so the handoff is visible in the **Activity** feed.

The timer only moves the request forward to the escalation stage. It is not an approval of the request: the escalated reviewers still have to approve before access is granted.

## How the timer is implemented

The escalation timer isn't separate machinery—it's an [OpalScript](/docs/opalscript-overview) automation attached to the **Escalation Timer** service user, built from the same primitives you use in your own request review scripts. Opal provisions one timer per timed stage and runs this script:

```python theme={null}
delay = context.get_escalation_delay_minutes()
if delay != None:
    actions.pause(delay)
    if context.is_pending_reviewer():
        actions.approve(comment="No reviewer responded in time - escalating.")
```

* `context.get_escalation_delay_minutes()` reads the wait time off the stage the timer sits on, so one script serves every escalation point. A `None` delay means the stage is no longer timed, and the script ends without acting.
* `actions.pause()` holds the run until the window expires. This is why `delay_minutes` tops out at 1440—it's the same bound that applies to any paused script.
* On resume, `context.is_pending_reviewer()` checks whether the stage is still waiting. If a reviewer responded during the window, the timer does nothing.

<Note>
  Opal owns this script. It doesn't appear in your OpalScript console and you can't edit it, but its executions show up on the Runs page like any other automation. See [Monitor runs](/docs/opalscript-editor#monitor-runs).
</Note>

## Configure escalation with Terraform

Escalation is an attribute of a reviewer stage, not an object of its own. In Terraform you attach an `escalation` block to the stage that should time out, inside `reviewer_stages` on a `request_configurations` entry. Opal derives the escalation stage from that block—you never declare the escalation stage yourself.

Escalation is available on `opal_resource`, `opal_group`, and `opal_configuration_template`, and is readable from the matching data sources. It requires **provider version 3.7.4 or later**. See [Use Terraform with Opal](/docs/use-terraform-with-opal) to install the provider.

<CodeGroup>
  ```hcl escalation.tf theme={null}
  resource "opal_group" "prod_db_access" {
    name       = "prod-db-access"
    group_type = "OPAL_GROUP"
    app_id     = "3ecff5db-e3fc-40c7-bb36-ff202f033095" // Retrieved from Opal dashboard
    visibility = "GLOBAL"

    request_configurations = [
      {
        priority               = 0
        allow_requests         = true
        auto_approval          = false
        require_mfa_to_request = false
        require_support_ticket = false

        reviewer_stages = [
          {
            // A stage with escalation must use OR. The provider defaults to
            // AND, so you have to set this explicitly.
            operator  = "OR"
            owner_ids = ["b36e5198-3e15-4769-a321-00db76ac9873"] // Database owners

            escalation = {
              delay_minutes = 240
              owner_ids     = ["5abab647-63e1-4cfa-84bb-235ae50bc5b1"] // SRE on-call
              user_ids      = ["7552a605-a334-4cfc-86fe-6f003cb0055a"] // Incident commander
            }
          }
        ]
      }
    ]
  }
  ```
</CodeGroup>

After `terraform apply`, the group has two stages in the Opal UI: the stage you declared, tagged with the wait time, and the **Escalation stage** below it. Terraform still tracks a single stage, because Opal folds the derived stage back into `escalation` when it reads the configuration. Subsequent plans are clean—you don't get a permanent diff for a stage you didn't write.

`escalation.owner_ids` and `escalation.user_ids` are sets, so reordering them doesn't produce a diff.

<Warning>
  `escalation` is optional-and-computed in the provider schema, so deleting the block from your configuration doesn't clear an escalation that already exists—Terraform keeps the value it has in state and the plan is empty. To take escalation off a stage, remove the **Escalation stage** in the Opal UI, or send the stage without `escalation` through the API.
</Warning>

## Configure escalation with the API

The API uses the same shape. `escalation` is a field on a reviewer stage, and reviewer stages live on a request configuration:

* [`PUT /resources`](/api-reference/resources/put-resources) and [`PUT /groups`](/api-reference/groups/put-groups) set `request_configurations` on a resource or group.
* [`POST /configuration-templates`](/api-reference/configuration-templates/post-configuration-templates) and [`PUT /configuration-templates`](/api-reference/configuration-templates/put-configuration-templates) set them on a configuration template.
* `GET /resources/{resource_id}`, `GET /groups/{group_id}`, and [`GET /resources/{resource_id}/reviewer-stages`](/api-reference/resources/get-resources-reviewer-stages) read them back.

```json theme={null}
{
  "request_configurations": [
    {
      "priority": 0,
      "allow_requests": true,
      "auto_approval": false,
      "require_mfa_to_request": false,
      "require_support_ticket": false,
      "reviewer_stages": [
        {
          "operator": "OR",
          "owner_ids": ["b36e5198-3e15-4769-a321-00db76ac9873"],
          "escalation": {
            "delay_minutes": 240,
            "owner_ids": ["5abab647-63e1-4cfa-84bb-235ae50bc5b1"],
            "user_ids": ["7552a605-a334-4cfc-86fe-6f003cb0055a"]
          }
        }
      ]
    }
  ]
}
```

Send one entry per approval hop. A stage that carries `escalation` becomes two stages internally, but reads collapse the pair back into one entry, so what you `GET` matches what you `PUT`. The **Escalation Timer** service user is managed by Opal and never appears in `service_user_ids`.

### Field reference

| Field | Type | Description |
| - | - | - |
| `delay_minutes` | integer, required | How long to wait for a response before escalating. Between 1 and 1440 (24 hours). |
| `owner_ids` | array of owner IDs | The owners to escalate to. |
| `user_ids` | array of user IDs | The individual users to escalate to. |

### Rules and common errors

Opal enforces the same rules on both paths. Except for the `delay_minutes` range, which the provider checks during `terraform plan`, the provider passes your configuration through, so a rejected apply reports the API's error verbatim.

* **The stage must use `OR`.** A stage with `escalation` and `operator` set to `AND` is rejected with `a reviewer stage with escalation must use the OR operator`. Opal adds the escalation timer to the stage as an extra reviewer; under `AND` the timer would become a required approver and every request would stall until the timeout. This mirrors the UI, where **Escalate if no response** is only offered on a stage set to **Any**.
* **Name at least one escalation target.** An `escalation` block with no `owner_ids` and no `user_ids` is rejected with `escalation must name at least one owner or user`.
* **Don't repeat the stage's own reviewers.** `escalation.owner_ids` and `escalation.user_ids` name *only* the people you're escalating to. The stage's own `owner_ids` and `service_user_ids` are added to the escalation stage automatically, and repeating them is rejected—`escalation owner_ids must not repeat the stage's own reviewers, they escalate automatically` for an owner, `escalation user_ids must not repeat the stage's own service users, they escalate automatically` for a service user. Both errors list the repeated IDs. Because they're unioned in rather than copied, removing a reviewer from the stage also removes them from the escalation—you don't have to remember to update both lists.
* **`delay_minutes` must be between 1 and 1440.** Terraform rejects anything outside that range at plan time; the API rejects it with `invalid escalation delay_minutes, must be between 1 and 1440, got 2880`. The timer is implemented as a pause of that length, so the bound is the same one that applies to a paused workflow.
* **Escalation must be enabled for your organization.** If it isn't, the request is rejected with `escalation is not enabled for this organization`. Contact Opal support to have it turned on.

## Examples

### Production database access — 4 hours

An engineer requests read access to the production database during an incident. First-line reviewers are the database owners; the escalation reviewers are the on-call SRE rotation.

* **Escalate after:** 4 hours
* **Escalate to:** SRE on-call

If the database owners don't respond within 4 hours, the on-call engineer picks it up. Nobody stays blocked during an incident waiting for a specific person to wake up.

### Marketing tool seat — 24 hours

A new hire requests a seat in a marketing SaaS tool. The team manager reviews these, and the escalation reviewers are the marketing ops team.

* **Escalate after:** 24 hours
* **Escalate to:** Marketing Ops

The window is deliberately loose. A seat request isn't urgent, but it also shouldn't disappear for two weeks while the manager is travelling.

### Admin role on a sensitive system — 8 hours, escalating to security

A developer requests an admin role on a system holding customer data. The resource owner reviews first; the escalation reviewers are the security team.

* **Escalate after:** 8 hours
* **Escalate to:** Security

Here escalation isn't about speed, it's about making sure a high-risk request always gets looked at by someone. It never quietly sits in one person's inbox.

## Related

* [Configure reviewers](/docs/configure-reviewers)
* [Notifications](/docs/notifications)
* [Use Terraform with Opal](/docs/use-terraform-with-opal)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.