
# Verbindung anlegen

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

Legt eine neue Verbindung in der Umgebung an. Verbindungen, die eine Protokollkonfiguration benötigen (`social`, `enterprise`), beginnen mit `inactive`; aktivieren Sie sie ausdrücklich mit `:activate`. Eine `enterprise`-Verbindung kann nicht direkt als `active` angelegt werden: Eine neue Verbindung kann noch keine erfolgreiche Test-Anmeldung haben. Einstellungen dürfen keine Geheimnisse enthalten. Eine Umgebung kann höchstens 100 Verbindungen und 20 generische Social-Verbindungen (`oidc_generic`, `oauth2_generic`) enthalten, eine Organisation höchstens 20 Verbindungen.

## Anfrage

JSON-Body (unbekannte Felder werden mit `400 unknown_field` abgelehnt):

| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| `key` | string | ja | Stabiler Konfigurationsschlüssel, `^[a-z0-9_-]{1,64}$`, in der Umgebung eindeutig, unveränderlich. |
| `type` | string | ja | `database`, `passwordless`, `passkey`, `social` oder `enterprise`. Unveränderlich. |
| `strategy` | string | ja | Strategie für den Typ, zum Beispiel `password`, `email_otp`, `google`, `saml`. Unveränderlich. |
| `name` | string | ja | 1–128 Zeichen, in der Umgebung eindeutig. |
| `display_name` | string | nein | 1–128 Zeichen, die auf den Anmeldebildschirmen angezeigt werden. |
| `status` | string | nein | `active` oder `inactive`. Standard ist `active` für `database`, `passwordless`, `passkey` und `inactive` für `social`, `enterprise`. |
| `org_id` | string | nein | Besitzende Organisation (`org_…`), nur für `enterprise`. Wenn weggelassen: eine Umgebungsverbindung. Unveränderlich. |
| `allowed_email_domains` | array of strings | nein | E-Mail-Domains, die über Home-Realm-Discovery dieser Verbindung zugeordnet werden: IDNA-A-Label, Kleinschreibung (zum Beispiel `xn--bcher-kva.example`), eindeutig, höchstens 100; nur für `enterprise` nicht leer. Bei einer Organisationsverbindung muss jede Domain eine verifizierte Domain der Organisation sein, und eine `active` Organisationsverbindung benötigt mindestens eine. |
| `allow_external_domains` | boolean | nein | Auch Benutzer anmelden, deren E-Mail-Domain nicht aufgeführt ist. Nur `enterprise`; Standard `false`. |
| `jit_mode` | string | nein | Just-in-Time-Provisioning: `create` legt den Benutzer bei der ersten Anmeldung an, `create_and_update` aktualisiert zusätzlich das Profil aus dem Identitätsanbieter, `off` erfordert ein bestehendes Konto. Nur `enterprise`. Standard ist `create_and_update` für eine Umgebungsverbindung und `off` für eine Organisationsverbindung. |
| `email_trust` | boolean | nein | Der vom Identitätsanbieter bestätigten E-Mail-Adresse für die automatische Kontoverknüpfung und das Just-in-Time-Provisioning vertrauen. Nur `enterprise`; Standard `false`. Bei einer Organisationsverbindung erfordert `true` ein nicht leeres `allowed_email_domains`, in dem jede Domain eine verifizierte Domain der Organisation ist. |
| `default_role_ids` | array of strings | nein | Umgebungsrollen (`rol_…`, eindeutig, höchstens 10), die der Mitgliedschaft zugewiesen werden, die eine Just-in-Time-Anmeldung anlegt, zusätzlich zur Standardrolle der Umgebung und den Gruppenrollenzuordnungen der Verbindung. Nur bei einer Organisations-`enterprise`-Verbindung. Jede Rolle muss eine nicht privilegierte Umgebungsrolle dieser Umgebung sein; die Rolle einer anderen Umgebung, eine Organisationsrolle, eine privilegierte Rolle oder eine fehlende Rolle wird abgelehnt. Eine später gelöschte oder privilegierte Rolle wird bei der Anmeldung übersprungen. |
| `trust_idp_mfa` | boolean | nein | Die vom Identitätsanbieter bestätigte Multi-Faktor-Authentifizierung (SAML-Authentication-Context, OIDC `amr`/`acr`) als MFA-Nachweis der Sitzung akzeptieren. Nur `enterprise`; Standard `false`. |
| `settings` | object | nein | Einstellungen ohne Geheimnisse, bis zu 32 KiB. Schlüssel oder Werte, die wie Geheimnisse aussehen, werden abgelehnt. |
| `Idempotency-Key` | header | nein | Optionaler Schlüssel (16–64 Zeichen), der eine wiederholte Anfrage sicher macht. |

## Antwort

`201 Created` mit der Verbindung und einem `ETag`-Header, der deren Version enthält.

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

## Passkey-Einstellungen

Bei `type: "passkey"` akzeptiert das Objekt `settings` nur die unten aufgeführten Schlüssel. Jeder andere Schlüssel, ein falscher Typ oder ein Wert außerhalb des zulässigen Bereichs wird mit `422 validation_failed` und einem `errors[].pointer` wie `/settings/max_credentials_per_user` abgelehnt. Der Wert `null` steht für den Standard. Fehler in der Struktur werden vor Planfehlern gemeldet.

| Schlüssel | Typ | Hinweise |
|---|---|---|
| `rp_id` | string | Fehlt, ist `null` oder leer: Jeder Anmeldehost ist seine eigene rpId. Andernfalls der Hostname einer `active` benutzerdefinierten Domain der Umgebung (Kleinschreibung, IDNA-A-Label); jeder andere Wert ergibt `422 validation_failed` (`/settings/rp_id`). Sobald die Umgebung Passkeys hat, ändern Sie ihn mit [rpId für Passkeys ändern](change-rp-id.md). |
| `rp_name` | string | Bis zu 128 Zeichen. |
| `passkey_first_factor_enabled`, `security_key_second_factor_enabled` | boolean | Standard `true`. |
| `user_verification` | string | `preferred` (Standard) oder `required`. |
| `max_credentials_per_user` | integer | 1–20, Standard 20; Dezimalzahlen werden abgelehnt. |
| `require_device_bound_passkeys` | boolean | Planabhängig: `true` erfordert Startup. |
| `attestation_required` | boolean | Planabhängig: `true` erfordert Enterprise. |
| `aaguid_allowlist`, `aaguid_blocklist` | array of UUID strings | Jeweils höchstens 500 eindeutige Einträge (Fehler verweisen auf `/settings/<key>/<index>`). Planabhängig: Eine nicht leere Liste erfordert Enterprise. |
| `related_origins` | array of strings | Ursprünge im Format `https://host[:port]`. Planabhängig: Eine nicht leere Liste erfordert Enterprise. |
| `passkey_enrollment_prompt` | string | `off` oder `after_login` (Standard). |

Das Schreiben eines nicht standardmäßigen Werts eines planabhängigen Schlüssels schlägt mit `403 plan_feature_unavailable` fehl und enthält die Member `feature` und `required_plan`. Solange die Planberechtigungen nicht verfügbar sind, können diese Schlüssel in keiner Umgebung gesetzt werden; ein Standardwert (`false`, `[]`, `null`) oder das Entfernen des Schlüssels ist immer erlaubt. Ist das Feature verfügbar, muss jeder Eintrag in `related_origins` eine aktive, verifizierte benutzerdefinierte Domain der Umgebung sein, andernfalls schlägt die Anfrage mit `422 related_origin_not_verified` fehl.

## Fehler

| HTTP | Code | Wann |
|---|---|---|
| 400 | `unknown_field` | Der Body enthält ein unbekanntes Feld. |
| 403 | `quota_exceeded` | Ein Verbindungslimit der Umgebung oder der Organisation (`org_id`) ist erreicht. |
| 409 | `conflict` | Eine Verbindung mit demselben `key` oder `name` existiert bereits. |
| 409 | `connection_test_required` | Die Anfrage legt eine `enterprise`-Verbindung mit `status: active` an; es wird nichts geschrieben. Legen Sie sie als `inactive` an, testen Sie die Anmeldung und aktivieren Sie sie dann mit `:activate`. |
| 415 | `unsupported_media_type` | Der Content-Type ist nicht `application/json`. |
| 422 | `validation_failed` | Ein Feld fehlt oder ist ungültig; `errors[].pointer` benennt es (zum Beispiel `/settings/client_secret` für geheimnisartige Einstellungen). |
| 422 | `related_origin_not_verified` | Ein zugehöriger Ursprung ist keine verifizierte benutzerdefinierte Domain der Umgebung. |
| 403 | `plan_feature_unavailable` | Der Plan enthält eine von Ihnen gesetzte Einstellung nicht (Member `feature`, `required_plan`), zum Beispiel `attestation_required`. |
| 401 | `unauthenticated` | Die Anmeldedaten fehlen, sind ungültig oder widerrufen. |
| 403 | `insufficient_permissions` | Die Anmeldedaten haben nicht die erforderliche Berechtigung. |
| 429 | `rate_limited` | Das Ratenlimit wurde überschritten; wiederholen Sie die Anfrage nach der Zeitspanne `Retry-After`. |
| 503 | `service_unavailable` | Eine Abhängigkeit ist vorübergehend nicht verfügbar; wiederholen Sie die Anfrage nach der Zeitspanne `Retry-After`. |

## Beispiel

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