
# Reemplazar la política de edad

`PUT /v1/age-policy` · permiso `identity-settings:write`

Escribe la política de edad completa. `If-Match` es obligatorio (`428` si falta, `412` si no coincide); una política que nunca se escribió tiene la versión `"0"`. Reglas: `min_age` y los umbrales por país están entre 13 y 21; los códigos de país son ISO 3166-1 alpha-2 (en mayúsculas), con un máximo de 250 entradas; `parental_consent_below` es `null` o 1..`min_age`; `store_birthdate` puede ser `true` solo cuando `mode` es `birthdate`. La política se aplica solo a los registros posteriores; los usuarios existentes no cambian.

## Solicitud

Cuerpo JSON (los campos desconocidos se rechazan con `400 unknown_field`):

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `If-Match` | header | sí | Encabezado: el valor `ETag` de `GET /v1/age-policy`. |
| `mode` | string | sí | `off`, `attestation` (pregunta neutral de año y mes de nacimiento, el valor no se almacena) o `birthdate`. |
| `min_age` | integer | sí | 13-21. |
| `country_min_age` | object | sí | Código de país → umbral (13-21); los países sin valor usan `min_age`. |
| `country_source` | string | sí | `ip` o `declared`. |
| `under_age_action` | string | sí | `block` o `create_minor`. |
| `parental_consent_below` | integer \| null | no | `null` (sin consentimiento parental) o 1..`min_age`. |
| `pending_consent_action` | string | sí | `block_sign_in` o `issue_limited`. |
| `store_birthdate` | boolean | sí | `true` solo con `mode: birthdate`. |
| `applies_to` | string | sí | `self_signup` o `all`. |

## Respuesta

`200 OK` con la política y el encabezado `ETag`.

```json
{
  "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

Mientras `mode` no sea `off`, los ID tokens, access tokens y la respuesta de UserInfo incluyen el `age_group` del usuario (`adult`, `minor` o `unknown`) independientemente de los scopes solicitados, y `parental_consent_status` (`not_required`, `pending`, `granted`, `denied` o `revoked`) cuando tiene un valor. Con `mode: off` no se emite ninguno de los dos claims. Solo KeenAuth escribe ambos (tus hooks y plantillas de claims no pueden establecerlos); cámbialos con `PATCH /v1/users/{user_id}`.

Con `pending_consent_action: block_sign_in`, un usuario cuyo `parental_consent_status` es `pending`, `denied` o `revoked` no recibe ningún token: la solicitud de autorización termina con `access_denied` y el endpoint de token responde `invalid_grant` (`Parental consent has not been granted.`) para las concesiones de código de autorización, código de dispositivo y refresh token. Un refresh token no se revoca; vuelve a funcionar cuando el consentimiento sea `granted`. Con `issue_limited` se emiten tokens y tu aplicación restringe al usuario según los claims.

## Errores

| HTTP | código | Cuándo |
|---|---|---|
| 400 | `unknown_field` | El cuerpo contiene un campo desconocido. |
| 412 | `precondition_failed` | `If-Match` no coincide con la versión actual. |
| 415 | `unsupported_media_type` | El tipo de contenido no es `application/json`. |
| 422 | `validation_failed` | Falta un campo (`required`) o incumple alguna regla anterior; el puntero indica el campo, por ejemplo `/country_min_age/DE`. |
| 428 | `precondition_required` | Falta el encabezado `If-Match`. |
| 401 | `unauthenticated` | La credencial falta, no es válida o fue revocada. |
| 403 | `insufficient_permissions` | La credencial no tiene el permiso requerido. |
| 429 | `rate_limited` | Se superó el límite de frecuencia; reintenta después del período `Retry-After`. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintenta después del período `Retry-After`. |

## Ejemplo

```bash
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"}'
```
