
# Update the attack protection settings

`PATCH /v1/attack-protection` · permission `attack-protection:write`

Updates the attack protection object with a JSON merge patch (`application/merge-patch+json`, RFC 7396). `If-Match` is required: missing gives `428`, a stale `ETag` gives `412`. A patch that changes nothing writes nothing and keeps the `ETag`.

## Request

| Field | Type | Required | Description |
|---|---|---|---|
| `If-Match` | header | yes | The `ETag` of the object as last read. Missing: `428`; stale: `412`. |
| body | object | yes | The merge patch. Fields are listed in [Replace the attack protection settings](put.md). |

## Behavior

- Sections merge recursively: send only the fields you change.
- `null` removes a field or a whole section, which returns it to its default. Lists such as `allowlist` and `allowed_countries` are replaced as a whole.
- The merged object is checked exactly as a `PUT` body.

## Response

`200 OK` with the object, as in [Get the attack protection settings](get.md), and the new `ETag`.

## Errors

| HTTP | code | When |
|---|---|---|
| 400 | `unknown_field` | A section or field is not part of the object. `errors[]` lists the pointers. |
| 400 | `malformed_request` | The body is not valid JSON, or the `If-Match` header is malformed. |
| 401 | `unauthenticated` | The credential is missing, invalid or revoked. |
| 403 | `insufficient_permissions` | The credential lacks `attack-protection:write`. |
| 403 | `plan_feature_unavailable` | `risk_policy.mode` is `enforce` and the plan does not include it; the body carries `feature` and `required_plan`. |
| 412 | `precondition_failed` | The object changed since you read it. Read it again and retry. |
| 413 | `payload_too_large` | The object is larger than 32 KiB. |
| 422 | `validation_failed` | A value has the wrong type or is out of range, or `block_threshold` is not greater than `challenge_threshold`. `errors[]` lists the pointers. |
| 422 | `below_security_floor` | A value is below the security floor. Each `errors[]` entry names the field and its floor in `detail`. |
| 422 | `allowlist_range_too_broad` | An allowlist prefix is wider than /8 (IPv4) or /32 (IPv6). |
| 428 | `precondition_required` | `If-Match` is missing. |
| 429 | `rate_limited` | The rate limit was exceeded; retry after the `Retry-After` period. |
| 503 | `service_unavailable` | A dependency is temporarily unavailable; retry after the `Retry-After` period. |

When several kinds of error are present, only the most severe kind is returned, in this order: `unknown_field`, `validation_failed`, `below_security_floor`, `allowlist_range_too_broad`, `plan_feature_unavailable`. The written values are never echoed back.

## Example

```bash
curl -X PATCH https://api.eu.keenauth.com/v1/attack-protection \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -H 'If-Match: "3f1c9a0b5d7e2a4c6b8d0e1f2a3b4c5d"' \
  -d '{"risk_policy": {"block_threshold": 9}, "sms_protection": null}'
```
