Replace the age policy

PUT /v1/age-policy · permission identity-settings:write

Writes the whole age policy. If-Match is required (428 when missing, 412 when it does not match); a policy that was never written has version "0". Rules: min_age and country thresholds are 13-21; country codes are ISO 3166-1 alpha-2 (uppercase), at most 250 entries; parental_consent_below is null or 1..min_age; store_birthdate can be true only when mode is birthdate. The policy applies to later sign-ups only; existing users do not change.

Request

JSON body (unknown fields are rejected with 400 unknown_field):

Field Type Required Description
If-Match header yes Header: the ETag value from GET /v1/age-policy.
mode string yes off, attestation (neutral birth year and month question, value not stored) or birthdate.
min_age integer yes 13-21.
country_min_age object yes Country code → threshold (13-21); missing countries use min_age.
country_source string yes ip or declared.
under_age_action string yes block or create_minor.
parental_consent_below integer | null no null (no parental consent) or 1..min_age.
pending_consent_action string yes block_sign_in or issue_limited.
store_birthdate boolean yes true only with mode: birthdate.
applies_to string yes self_signup or all.

Response

200 OK with the policy and the ETag header.

{
  "mode": "attestation",
  "min_age": 16,
  "country_min_age": {"DE": 16, "FR": 15, "IT": 14},
  "country_source": "ip",
  "under_age_action": "block",
  "parental_consent_below": null,
  "pending_consent_action": "block_sign_in",
  "store_birthdate": false,
  "applies_to": "self_signup",
  "updated_at": "2026-10-05T09:30:00.123456Z"
}

Tokens

While mode is not off, ID tokens, access tokens and the UserInfo response carry the user's age_group (adult, minor or unknown) whatever the requested scopes, and parental_consent_status (not_required, pending, granted, denied or revoked) when it has a value. With mode: off neither claim is issued. Both are written only by KeenAuth (your hooks and claim templates cannot set them); change them with PATCH /v1/users/{user_id}.

Under pending_consent_action: block_sign_in, a user whose parental_consent_status is pending, denied or revoked gets no token: the authorization request ends with access_denied and the token endpoint answers invalid_grant (Parental consent has not been granted.) for the authorization code, device code and refresh token grants. A refresh token is not revoked; it works again once the consent is granted. Under issue_limited tokens are issued and your application restricts the user from the claims.

Errors

HTTP code When
400 unknown_field The body contains an unknown field.
412 precondition_failed If-Match does not match the current version.
415 unsupported_media_type The content type is not application/json.
422 validation_failed A field is missing (required) or breaks a rule above; the pointer names the field, for example /country_min_age/DE.
428 precondition_required The If-Match header is missing.
401 unauthenticated The credential is missing, invalid or revoked.
403 insufficient_permissions The credential lacks the required permission.
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.

Example

curl -X PUT https://api.eu.keenauth.com/v1/age-policy \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "0"' \
  -d '{"mode":"attestation","min_age":16,"country_min_age":{"DE":16,"FR":15},"country_source":"ip","under_age_action":"block","parental_consent_below":null,"pending_consent_action":"block_sign_in","store_birthdate":false,"applies_to":"self_signup"}'