
# Crear una conexión

`POST /v1/connections` · permiso `connections:write`

Registra una nueva conexión en el entorno. Las conexiones que necesitan configuración de protocolo (`social`, `enterprise`) comienzan como `inactive`; actívelas explícitamente con `:activate`. Una conexión `enterprise` no puede crearse como `active`: una conexión nueva todavía no puede tener un inicio de sesión de prueba correcto. La configuración no debe contener secretos. Un entorno puede tener como máximo 100 conexiones y 20 conexiones sociales genéricas (`oidc_generic`, `oauth2_generic`), y una organización como máximo 20 conexiones.

## Solicitud

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

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `key` | string | sí | Clave de configuración estable, `^[a-z0-9_-]{1,64}$`, única en el entorno, inmutable. |
| `type` | string | sí | `database`, `passwordless`, `passkey`, `social` o `enterprise`. Inmutable. |
| `strategy` | string | sí | Estrategia para el tipo, por ejemplo `password`, `email_otp`, `google`, `saml`. Inmutable. |
| `name` | string | sí | De 1 a 128 caracteres, única en el entorno. |
| `display_name` | string | no | De 1 a 128 caracteres que se muestran en las pantallas de inicio de sesión. |
| `status` | string | no | `active` o `inactive`. Por defecto es `active` para `database`, `passwordless`, `passkey`, e `inactive` para `social`, `enterprise`. |
| `org_id` | string | no | Organización propietaria (`org_…`), solo `enterprise`. Si se omite: una conexión del entorno. Inmutable. |
| `allowed_email_domains` | array de strings | no | Dominios de correo electrónico dirigidos a esta conexión mediante el descubrimiento de dominio de inicio de sesión: etiqueta A de IDNA, en minúsculas (por ejemplo `xn--bcher-kva.example`), únicos, como máximo 100; solo puede tener valores para `enterprise`. En una conexión de organización, cada dominio debe ser un dominio verificado de la organización, y una conexión de organización `active` necesita al menos uno. |
| `allow_external_domains` | boolean | no | Permite también el inicio de sesión de usuarios cuyo dominio de correo electrónico no aparezca en la lista. Solo `enterprise`; por defecto `false`. |
| `jit_mode` | string | no | Aprovisionamiento just-in-time: `create` crea el usuario en su primer inicio de sesión, `create_and_update` además actualiza el perfil desde el proveedor de identidad, `off` requiere una cuenta existente. Solo `enterprise`. Por defecto es `create_and_update` para una conexión del entorno y `off` para una conexión de organización. |
| `email_trust` | boolean | no | Confía en la dirección de correo electrónico que afirma el proveedor de identidad para la vinculación automática de cuentas y el aprovisionamiento just-in-time. Solo `enterprise`; por defecto `false`. En una conexión de organización, `true` requiere un `allowed_email_domains` no vacío en el que cada dominio sea un dominio verificado de la organización. |
| `default_role_ids` | array de strings | no | Roles del entorno (`rol_…`, únicos, como máximo 10) concedidos a la membresía que crea un inicio de sesión just-in-time, además del rol predeterminado del entorno y de las asignaciones de roles por grupo de la conexión. Solo en una conexión `enterprise` de organización. Cada rol debe ser un rol del entorno no privilegiado de este entorno; se rechaza un rol de otro entorno, un rol de organización, un rol privilegiado o un rol inexistente. Un rol eliminado o convertido en privilegiado más tarde se omite en el inicio de sesión. |
| `trust_idp_mfa` | boolean | no | Acepta la autenticación multifactor que afirma el proveedor de identidad (contexto de autenticación de SAML, `amr`/`acr` de OIDC) como prueba de MFA de la sesión. Solo `enterprise`; por defecto `false`. |
| `settings` | object | no | Configuración sin secretos, de hasta 32 KiB. Las claves o valores que parecen secretos se rechazan. |
| `Idempotency-Key` | header | no | Clave opcional (de 16 a 64 caracteres) que hace seguro reintentar una solicitud. |

## Respuesta

`201 Created` con la conexión y un encabezado `ETag` que contiene su versión.

```json
{
  "id": "conn_0k3v8m1q2x9dz4h7s5t6w0y1ab",
  "key": "google",
  "type": "social",
  "strategy": "google",
  "name": "Google",
  "display_name": "Sign in with Google",
  "status": "inactive",
  "org_id": null,
  "allowed_email_domains": [],
  "allow_external_domains": false,
  "jit_mode": null,
  "email_trust": null,
  "default_role_ids": [],
  "trust_idp_mfa": null,
  "allow_idp_initiated": null,
  "idp_initiated_default_application_id": null,
  "idp_initiated_allowed_application_ids": [],
  "settings": {},
  "created_at": "2026-10-05T09:30:00.123456Z",
  "updated_at": "2026-10-05T09:30:00.123456Z"
}
```

## Configuración de claves de acceso

Para `type: "passkey"`, el objeto `settings` acepta solo las claves indicadas a continuación. Cualquier otra clave, un tipo incorrecto o un valor fuera de rango se rechaza con `422 validation_failed` y un `errors[].pointer` como `/settings/max_credentials_per_user`. Un valor `null` significa el valor predeterminado. Los errores de forma se notifican antes que los errores de plan.

| Clave | Tipo | Notas |
|---|---|---|
| `rp_id` | string | Ausente, `null` o vacío: cada host de inicio de sesión es su propio rpId. De lo contrario, el nombre de host de un dominio personalizado `active` del entorno (en minúsculas, etiqueta A de IDNA); cualquier otro valor produce `422 validation_failed` (`/settings/rp_id`). Una vez que el entorno tenga claves de acceso, cámbielo con [Cambiar el rpId de la clave de acceso](change-rp-id.md). |
| `rp_name` | string | Hasta 128 caracteres. |
| `passkey_first_factor_enabled`, `security_key_second_factor_enabled` | boolean | Por defecto `true`. |
| `user_verification` | string | `preferred` (predeterminado) o `required`. |
| `max_credentials_per_user` | integer | De 1 a 20, por defecto 20; las fracciones se rechazan. |
| `require_device_bound_passkeys` | boolean | Condicionado por el plan: `true` requiere Startup. |
| `attestation_required` | boolean | Condicionado por el plan: `true` requiere Enterprise. |
| `aaguid_allowlist`, `aaguid_blocklist` | array de strings UUID | Como máximo 500 entradas únicas cada una (los errores apuntan a `/settings/<key>/<index>`). Condicionado por el plan: una lista no vacía requiere Enterprise. |
| `related_origins` | array de strings | Orígenes `https://host[:port]`. Condicionado por el plan: una lista no vacía requiere Enterprise. |
| `passkey_enrollment_prompt` | string | `off` o `after_login` (predeterminado). |

Escribir un valor no predeterminado de una clave condicionada por el plan falla con `403 plan_feature_unavailable` y los miembros `feature` y `required_plan`. Hasta que las entitlements del plan estén disponibles, estas claves no pueden configurarse en ningún entorno; escribir un valor predeterminado (`false`, `[]`, `null`) o eliminar la clave siempre está permitido. Cuando la función esté disponible, cada entrada de `related_origins` debe ser un dominio personalizado verificado y activo del entorno; de lo contrario, la solicitud falla con `422 related_origin_not_verified`.

## Errores

| HTTP | código | Cuándo |
|---|---|---|
| 400 | `unknown_field` | El cuerpo contiene un campo desconocido. |
| 403 | `quota_exceeded` | Se ha alcanzado un límite de conexiones del entorno o de la organización (`org_id`). |
| 409 | `conflict` | Ya existe una conexión con la misma `key` o `name`. |
| 409 | `connection_test_required` | La solicitud crea una conexión `enterprise` con `status: active`; no se escribe nada. Créela como `inactive`, pruebe su inicio de sesión y luego actívela con `:activate`. |
| 415 | `unsupported_media_type` | El tipo de contenido no es `application/json`. |
| 422 | `validation_failed` | Falta un campo o no es válido; `errors[].pointer` lo indica (por ejemplo `/settings/client_secret` para configuraciones con apariencia de secreto). |
| 422 | `related_origin_not_verified` | Un origen relacionado no es un dominio personalizado verificado del entorno. |
| 403 | `plan_feature_unavailable` | El plan no incluye una configuración que usted ha establecido (miembros `feature`, `required_plan`), por ejemplo `attestation_required`. |
| 401 | `unauthenticated` | La credencial falta, no es válida o ha sido revocada. |
| 403 | `insufficient_permissions` | La credencial no tiene el permiso requerido. |
| 429 | `rate_limited` | Se ha superado el límite de frecuencia; reintente después del período indicado en `Retry-After`. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintente después del período indicado en `Retry-After`. |

## Ejemplo

```bash
curl -X POST https://api.eu.keenauth.com/v1/connections \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key":"google","type":"social","strategy":"google","name":"Google"}'
```
