Replace the attack protection settings

PUT /v1/attack-protection · permission attack-protection:write

Replaces the whole attack protection object (application/json). Sections and fields you leave out return to their defaults. If-Match is required: missing gives 428, a stale ETag gives 412. Writing the same object again writes nothing and keeps the ETag. To change a few fields, use PATCH.

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 configured object, at most 32 KiB. Each section is optional; each field inside a section is optional.
Field Type Range or values Default Floor
brute_force.mode string log, enforce enforce
brute_force.max_failures integer 5-100 20 at most 100
brute_force.window_minutes integer 5-60 15
brute_force.lock_minutes integer 5-60 15 at least 5
brute_force.notify_user boolean true
suspicious_ip_throttling.mode string log, enforce enforce
suspicious_ip_throttling.login_failures_per_ip_per_day integer 10-10000 100
suspicious_ip_throttling.signups_per_ip_per_hour integer 1-20 20 at most 20
suspicious_ip_throttling.allowlist string[] up to 100 CIDR prefixes; IPv4 no wider than /8, IPv6 no wider than /32 []
credential_stuffing.mode string log, enforce enforce
credential_stuffing.attack_mode_auto boolean true
credential_stuffing.attack_mode_duration_minutes integer 15-240 30
bot_protection.mode string off, risk_based, always risk_based
bot_protection.provider string altcha altcha
breached_password.on_signup string block block block
breached_password.on_password_change string block block block
breached_password.on_login string require_reset, notify require_reset
breached_password.notify_user boolean true
signup_protection.block_disposable_email boolean true
signup_protection.block_email_subaddress boolean false
signup_protection.max_signups_per_device_30d integer 1-50 3
sms_protection.allowed_countries string[] up to 250 ISO 3166-1 alpha-2 codes [] (plan default)
sms_protection.daily_cap integer | null 1-1000000; null is the plan value null
sms_protection.conversion_breaker boolean true true
risk_policy.mode string log, enforce (enforce needs the Startup plan or higher) log
risk_policy.challenge_threshold integer 3-8 4
risk_policy.block_threshold integer | null 6-12, greater than challenge_threshold; null is off null
risk_policy.notify_user_on_new_device boolean true

A value outside the security floor is refused with 422 below_security_floor even though it is inside the field's general range: the floor protects every environment and cannot be lowered.

Behavior

Response

200 OK with the object, as in Get the attack protection settings, 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

curl -X PUT https://api.eu.keenauth.com/v1/attack-protection \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "3f1c9a0b5d7e2a4c6b8d0e1f2a3b4c5d"' \
  -d '{"brute_force": {"max_failures": 10, "lock_minutes": 30}, "bot_protection": {"mode": "always"}}'