---
title: "Security"
description: "Every operation in this group: what it accepts, what it returns and the errors it can answer with."
url: "https://openemail.uk/docs/api/reference/security"
area: "API"
category: "Reference"
---

# Security

Every operation in this group: what it accepts, what it returns and the errors it can answer with.

## Operations

Verification codes for OAuth access tokens. A few operations change who can reach the workspace or where its mail goes, and an app acting for a person has to confirm who they are before it does them, exactly as the app does: ask for a code, send the code the person typed, and repeat the call. API keys are never asked for a code.

### `GET /security/step-up`

Whether this access token is verified

Needs no scope, and is for OAuth access tokens only: an API key is refused with 400 `step_up_not_applicable`, because a key is never asked for a code.

Some operations change who can reach the workspace or where its mail goes, and the app asks the person to confirm who they are before it does them. An app acting for that person is held to the same rule: called with an OAuth access token, each of those operations answers 403 `step_up_required` until the token is verified, and one verification lasts 60 minutes. `method` says which kind of code to ask the person for.

- Needs an API key or an OAuth access token, and no scope.

**Returns**

- `200` `StepUpStatus`: Whether the token is verified, until when, and which kind of code it takes.

**Errors**

- `400`: `step_up_not_applicable`: the call was made with an API key, which is never asked for a code.
- The errors every operation can return: `401`, `403`, `404`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: SDK [`security.stepUpStatus()`](https://openemail.uk/docs/sdk/reference/security#stepUpStatus).

### `POST /security/step-up`

Ask for a verification code

Needs no scope, and is for OAuth access tokens only. Starts a verification for the person the token acts for.

With `method` `email`, a six-digit code goes to the address they sign in with and works for 10 minutes. The email names the app and, when a call from it was refused for want of a code in the last 15 minutes, the last thing it asked to do. It also tells them that the code is not tied to that one call: for 60 minutes it lets the app make every change that needs a code, within the access they gave it. With `totp`, nothing is sent: they read the code off their authenticator app, or use one of their backup codes. Either way, send what they type to `POST /security/step-up/verify`.

A code already waiting is reused rather than sent again, which `reused` reports; pass `resend: true` to send a fresh one. A code that took 5 wrong tries is never reused, so a call without `resend` then starts a fresh one.

Each app has its own allowance for each person: 5 codes an hour and 20 in 24 hours. It is separate from every other app's and from the 10 an hour the person has on the website. After 10 wrong codes for one app in 24 hours, verification for that app is paused, and after 20 wrong codes across all of the person's apps it is paused for every app. Either way this answers 429 `step_up_locked` until the time its message gives.

- Needs an API key or an OAuth access token, and no scope.

**Request body**

- `resend` (`boolean`): Send a new code even though one is still waiting, and stop the waiting one from working. Left out, a waiting code that still has tries left is reused and nothing is sent.

**Returns**

- `200` `StepUpChallenge`: How the person gets their code, and where it went.

**Errors**

- `400`: `step_up_not_applicable`: the call was made with an API key, which is never asked for a code.
- `429`: `step_up_throttled`: this app has asked for too many codes for this person. Each app has its own allowance of 5 codes an hour and 20 in 24 hours, and the message says when the next one can be asked for. It is separate from every other app's and from the 10 an hour the person has on the website, so one app never uses those up. `step_up_locked`: 10 wrong codes were sent for this app in the last 24 hours, so verification is paused for it. No code can be asked for or checked until the time the message gives after `paused until`, which is when the oldest of those wrong codes is 24 hours old. The same answer comes once the person's apps together have sent 20 wrong codes in 24 hours: verification is then paused for every app they connected, and removing an app does not lift that pause. Codes entered on the website are not affected. The person is told by email.
- `503`: `step_up_undeliverable`: the email with the code could not be sent just now. Try again in a moment.
- The errors every operation can return: `401`, `403`, `404`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: SDK [`security.beginStepUp()`](https://openemail.uk/docs/sdk/reference/security#beginStepUp).

### `POST /security/step-up/verify`

Send the verification code

Needs no scope, and is for OAuth access tokens only. Checks the code the person typed against the one `POST /security/step-up` started, and on a match verifies this token for 60 minutes. Every operation that answered `step_up_required` works until `elevatedUntil`, and so do the app's MCP tools that need a code.

A code takes at most 5 tries, and an authenticator code is accepted only once. Wrong codes also add up for the app: after 10 in 24 hours, verification for that app is paused (after 20 across all of the person's apps, for every app), even a correct code is refused until the time the 429 message gives, and the person is told by email. The verification belongs to the app the token was issued to, by its client ID, so a second app verifies separately.

- Needs an API key or an OAuth access token, and no scope.

**Request body**

- `code` (`string`, required, 6 to 12 characters): The six-digit code from the email or the authenticator app, or one of the backup codes, such as `abcde-12345`. Spaces are ignored. A backup code works once.

**Returns**

- `200` `StepUpVerification`: Verified, and until when.

**Errors**

- `400`: `step_up_code_invalid`: the code did not match, and the message says how many tries are left, counting down to whichever runs out first: this code's 5 tries, the 10 wrong codes the app may send in 24 hours, or the 20 wrong codes all of the person's apps together may send in 24 hours. An authenticator code is accepted once, so sending it again counts as a wrong code. `step_up_code_expired`: there is no code waiting, or it ran out after 10 minutes. Ask for a new one with `POST /security/step-up`. `step_up_not_applicable`: the call was made with an API key.
- `429`: `step_up_locked`: 5 wrong codes were sent for this code, so it no longer works. Ask for a new one with `POST /security/step-up`. A code that ran out of tries is never reused, so that call starts a fresh one even without `resend`. `step_up_locked`: 10 wrong codes were sent for this app in the last 24 hours, so verification is paused for it. No code can be asked for or checked until the time the message gives after `paused until`, which is when the oldest of those wrong codes is 24 hours old. The same answer comes once the person's apps together have sent 20 wrong codes in 24 hours: verification is then paused for every app they connected, and removing an app does not lift that pause. Codes entered on the website are not affected. The person is told by email.
- The errors every operation can return: `401`, `403`, `404`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: SDK [`security.verifyStepUp()`](https://openemail.uk/docs/sdk/reference/security#verifyStepUp).

### Objects

#### `StepUpChallenge`

`object`

- `object` (`string`, one of `"step_up_challenge"`)
- `method` (`string`, one of `"totp"`, `"email"`): `totp` when the person has two-factor authentication turned on: the code comes from their authenticator app, or is one of their backup codes. `email` otherwise: a six-digit code that works for 10 minutes is emailed to the address they sign in with.
- `sentTo` (`string`): Only when `method` is `email`: the address the code went to, masked to its first character and its domain, such as `a•••@example.com`.
- `reused` (`boolean`): True when a code sent earlier is still waiting and nothing new was sent. Pass `resend: true` to send a fresh one. A code that took 5 wrong tries is never reused, so the next call sends a fresh one without `resend`.

#### `StepUpStatus`

`object`

- `object` (`string`, one of `"step_up"`)
- `elevated` (`boolean`): Whether this access token may call the operations that need a verification code right now.
- `elevatedUntil` (`string`, nullable, format `date-time`): When the current verification runs out. Null when there is none.
- `method` (`string`, one of `"totp"`, `"email"`): `totp` when the person has two-factor authentication turned on: the code comes from their authenticator app, or is one of their backup codes. `email` otherwise: a six-digit code that works for 10 minutes is emailed to the address they sign in with.
- `minutes` (`integer`, one of `60`): How long one verification lasts, in minutes.

#### `StepUpVerification`

`object`

- `object` (`string`, one of `"step_up"`)
- `elevated` (`boolean`, one of `true`)
- `elevatedUntil` (`string`, format `date-time`): When this verification runs out, 60 minutes from now.
