
# Get the attack mode state

`GET /v1/attack-protection/attack-mode` · permission `attack-protection:read`

Returns whether the environment is in attack mode, and the attack mode periods that started in the last 90 days or are still open, newest first (at most 100; `more` tells you that older ones exist in that window).

While the environment is in attack mode, every password and one-time code step asks for a harder bot challenge, whatever `bot_protection.mode` says. Passkey and social sign-in are not challenged. A period opens in one of three ways:

- `auto`: failed sign-ins in a 5-minute window passed the trigger threshold (the larger of 50 and ten times the environment's median). In `log` mode of `credential_stuffing` the period is recorded with `would_have: true` and nothing is enforced, unless the window passes three times the threshold.
- `manual_customer`: you turned it on with [`activate`](activate-attack-mode.md).
- `manual_platform`: KeenAuth operations turned it on.

`active` is `true` only while an enforced period (`would_have: false`) covers the whole environment; `active_until` is then the latest end of those periods. A period whose end has passed but that the cleanup has not closed yet reads as ended at `ends_at`. Who opened a manual period is in the management audit log, not in this response.

## Response

`200 OK`

| Field | Type | Description |
|---|---|---|
| `active` | boolean | The environment is in attack mode now. |
| `active_until` | string (date-time) or null | When the current attack mode ends, unless an automatic period is extended; `null` when `active` is `false`. |
| `periods[]` | array | Periods, newest first. |
| `periods[].id` | string | Period id, prefixed `atm_`. |
| `periods[].scope` | string | `environment` or `org_domain`. |
| `periods[].org_domain` | string or null | The organization email domain of an `org_domain` period. |
| `periods[].trigger` | string | `auto`, `manual_customer` or `manual_platform`. |
| `periods[].would_have` | boolean | `true`: recorded in `log` mode, not enforced. |
| `periods[].trigger_value` | integer or null | Failed attempts in the 5-minute window that opened an `auto` period; `null` for manual periods. |
| `periods[].threshold` | integer or null | The threshold that window passed; `null` for manual periods. |
| `periods[].started_at` | string (date-time) | When the period started. |
| `periods[].ends_at` | string (date-time) | When the period ends by itself. An `auto` period is extended while the attack goes on. |
| `periods[].ended_at` | string (date-time) or null | When the period ended; `null` while it is open. |
| `more` | boolean | Older periods exist in the last 90 days. |

```json
{
  "active": true,
  "active_until": "2026-10-20T10:00:00Z",
  "periods": [
    {
      "id": "atm_01jaz8m5c3f6t9w2y4b7d0g3k6",
      "scope": "environment",
      "org_domain": null,
      "trigger": "manual_customer",
      "would_have": false,
      "trigger_value": null,
      "threshold": null,
      "started_at": "2026-10-20T09:00:00Z",
      "ends_at": "2026-10-20T10:00:00Z",
      "ended_at": null
    }
  ],
  "more": false
}
```

## Errors

Every error is an `application/problem+json` body with a stable `code`.

| HTTP | `code` | When |
|---|---|---|
| 401 | `unauthenticated` | The API key is missing, invalid or revoked. |
| 403 | `insufficient_permissions` | The key lacks the `attack-protection:read` permission. |
| 429 | `rate_limited` | The rate limit was exceeded; honor `Retry-After`. |
| 503 | `service_unavailable` | A dependency is temporarily unavailable; retry later. |

## Example

```bash
curl https://api.eu.keenauth.com/v1/attack-protection/attack-mode \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY"
```

CLI: `keenauth attack-protection attack-mode get`
