
# Crear o reemplazar una política de organización

`PUT /v1/organizations/{org_id}/policies/{policy_kind}` · permiso `organizations:write`

Reemplaza el objeto de configuración completo de un tipo de política. Una política de organización solo puede ser más estricta que la configuración del entorno. El control de plan lo aplica el servidor: los tipos `session` e `ip_allowlist` y la lista de permitidos de AAGUID requieren Enterprise (disponible en entornos de desarrollo). El contenido de la configuración se escribe en el registro de auditoría de gestión; el evento `organization.updated` no lo incluye.

## Solicitud

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `org_id` | path | sí | ID de la organización (`org_…`). |
| `policy_kind` | path | sí | Tipo de política: `authentication_methods`, `mfa`, `phishing_resistant`, `sso_enforcement`, `session` o `ip_allowlist`. Cualquier otro valor devuelve `404`. |
| `If-Match` | header | no | Obligatorio cuando la política existe (`ETag` de la última lectura); omítalo para crear la política. `*` coincide con una política existente. |
| `settings` | object | sí | El objeto de configuración del tipo. Campos por tipo: `authentication_methods`: `allowed_connection_ids` (`conn_…`), `allow_passkey`, `allow_password`; `mfa`: `required_for` (`none`, `admins`, `all`), `allowed_factors` (`totp`, `webauthn`, `sms`, `email`), `enrollment_grace_days` (0-30); `phishing_resistant`: `required_for`, `aaguid_allowlist` (como máximo 500); `sso_enforcement`: `mode` (`off`, `org_context`, `verified_domains`), `connection_ids` (`conn_…`), `bypass_user_ids` (`usr_…`, como máximo 2); `session`: `idle_minutes`, `absolute_minutes`, `refresh_idle_days`, `refresh_absolute_days`; `ip_allowlist`: `cidrs` (como máximo 100), `applies_to` (`admins`, `all`). |

Cuerpo de la solicitud:

```json
{
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  }
}
```

## Respuesta

`201 Created` cuando se creó la política, `200 OK` cuando se reemplazó; el cuerpo es la política y la respuesta incluye su nuevo `ETag`.

```json
{
  "organization_id": "org_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "policy_kind": "mfa",
  "schema_version": 1,
  "settings": {
    "required_for": "all",
    "allowed_factors": [
      "totp",
      "webauthn"
    ],
    "enrollment_grace_days": 7
  },
  "updated_at": "2026-10-12T09:30:00.123456Z",
  "version": 1
}
```

## Errores

| HTTP | code | Cuándo |
|---|---|---|
| 400 | `unknown_field` | El cuerpo tiene un campo que no está en el esquema o que es de solo lectura. |
| 403 | `plan_feature_unavailable` | El tipo o la lista de permitidos de AAGUID requiere un plan superior; el problema incluye `feature` y `required_plan`. |
| 404 | `not_found` | La organización no existe o el tipo es desconocido. |
| 409 | `domain_not_verified` | `sso_enforcement` con `mode` `verified_domains` requiere un dominio verificado en la organización. |
| 409 | `connection_test_required` | Activar `sso_enforcement` (desde `off` o desde la ausencia de política) requiere un inicio de sesión de prueba correcto de la configuración actual de al menos uno de los `connection_ids` en las últimas 24 horas. Ejecute una prueba de conexión (`POST /v1/connections/{connection_id}:test`) e inténtelo de nuevo. |
| 409 | `organization_pending_deletion` | La organización está pendiente de eliminación por autoservicio; cancele primero la eliminación. |
| 412 | `precondition_failed` | `If-Match` no coincide con la versión actual, o se envió mientras no existe ninguna política de este tipo, o otra solicitud creó la política mientras esta la creaba; vuelva a leer la política. |
| 422 | `validation_failed` | Un campo de configuración es desconocido o no válido; `errors[]` apunta a `/settings/…`. |
| 422 | `policy_weaker_than_parent` | La configuración es menos estricta que la del entorno; `errors[]` apunta al campo menos estricto. |
| 422 | `at_least_one_method` | `authentication_methods` no deja ningún método de inicio de sesión. |
| 428 | `precondition_required` | Falta `If-Match` mientras la política existe. |
| 401 | `unauthenticated` | Falta la credencial, no es válida o ha sido revocada. |
| 403 | `insufficient_permissions` | La credencial no tiene el permiso requerido. |
| 429 | `rate_limited` | Se superó el límite de frecuencia; reintente después del periodo de `Retry-After`. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintente después del periodo de `Retry-After`. |

## Ejemplo

```bash
curl -X PUT https://api.eu.keenauth.com/v1/organizations/org_01j9xk3m7q8r2s4t6v8w0y2z4a/policies/mfa \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"settings":{"required_for":"all","allowed_factors":["totp","webauthn"]}}'
```
