---
title: High-risk permissions
description: Permissions that are hard to undo, such as deleting a service. How users grant them, how long they last, and how to ask a user to confirm again.
---

Some permissions are hard to undo, for example deleting a service. A Zev
product marks those permissions as **high risk**. Your app can only request
one after ZevID approves it for your app on that product. Approval is per
app and per product, and a product marking a permission high risk never
approves it for anyone.

## What the user sees

When your authorize request includes a high-risk permission:

1. **They confirm it is them.** If the user has not signed in to ZevID in
   the last 5 minutes, ZevID asks for their password first. If they use
   two-factor authentication, it also asks for their authenticator code.
2. **The permission is labelled.** It shows a plain "High risk" label and
   one sentence explaining why.
3. **It starts unticked.** The user has to tick it on purpose.

The user can allow the connection without ticking it. The rest of the
connection works as normal.

> **Warning:**
> Never assume you received what you asked for. Read `scope` in the token
> response and check it before calling a high-risk route. A connection
> granted without the high-risk permission is a normal, valid connection.

## The confirmation window

A high-risk permission only works for a limited time after the user
confirms it:

- **30 days** by default.
- A product can set its own window, from 1 to 90 days.
- ZevID can set a shorter window for a particular app.

When the window ends, new tokens stop carrying the permission. The rest of
the connection keeps working. Access tokens issued before the end also
stop working for high-risk routes, because the product checks `hr_exp`.

## Token response fields

The [token response](/delegated-access/access-tokens) carries three fields
about high-risk permissions:

| Field | Value |
| --- | --- |
| `auth_strength` | `user_consent` when the token carries only standard permissions. `user_consent+recent_auth` when it carries at least one active high-risk permission. |
| `hr_exp` | The earliest end of an active high-risk permission, in epoch seconds. `null` when none is active. |
| `high_risk` | Every high-risk permission on the connection: `[{ scope, confirmed_at, expires_at, active }]`. `active: false` means its window ended. |

```json
{
  "scope": "zevcloud.deploy zevcloud.services.delete",
  "auth_strength": "user_consent+recent_auth",
  "hr_exp": 1763200000,
  "high_risk": [
    {
      "scope": "zevcloud.services.delete",
      "confirmed_at": "2026-10-20T08:00:00.000Z",
      "expires_at": "2026-11-19T08:00:00.000Z",
      "active": true
    }
  ]
}
```

The scope names here are examples. Use the ones your product publishes; see
[Scopes by product](/delegated-access/scopes).

## Asking the user to confirm again

Send the user through [authorize](/delegated-access/flow) with
`prompt=reconfirm`. `scope` is optional; when you send it, it must be a
subset of the connection's high-risk permissions. ZevID shows a short screen,
the user confirms it is them, and a fresh window starts. You then exchange the
code as usual. The connection's targets and other permissions do not change.

```text
https://api.zevauth.net/v1/partner/delegated/authorize
  ?publishable_key=pk_live_...
  &resource=zevcloud
  &prompt=reconfirm
  &redirect_uri=https%3A%2F%2Fapp.example.com%2Fzev%2Fcallback
  &state=...
  &code_challenge=...
  &code_challenge_method=S256
```

The user can also confirm again from **Apps and websites** in their ZevID
account.

Send the user through reconfirm when:

- A refresh returns `invalid_grant` with `reason: "high_risk_expired"`. The
  connection has nothing usable left except ended high-risk permissions.
- The product answers a call with `403 high_risk_confirmation_required`.
- `high_risk` shows the permission with `active: false` and the user wants
  to do something that needs it.

## When the user did not grant it

Design for a connection without the high-risk permission:

- Hide or disable the features that need it, and say why.
- Offer a button that sends the user through authorize again with the same
  scopes. An ended or never-granted high-risk permission comes back as an
  opt-in choice.
- Do not send the user through consent in a loop. Ask when they try to use
  the feature.

Your users get an email from ZevID 3 days before a window ends, but only if
your app used the permission in the last 30 days.
