
# Erstelle eine API

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

Erstellt eine API in der Umgebung. `key`, `identifier` und `name` sind erforderlich; ausgelassene optionale Felder erhalten ihre Standardwerte. Der Identifier ist eindeutig in der Umgebung (eine gelöschte API behält ihren Identifier für 90 Tage reserviert). Die Antwort enthält ein `ETag` mit der aktuellen Version. Sende einen `Idempotency-Key`-Header, um Wiederholungen sicher zu machen.

## Anfrage

Body `application/json`. Unbekannte Felder werden abgelehnt.

| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
| `key` | string | ja | Stabiler Konfigurationsschlüssel, `^[a-z0-9_-]{1,64}$`. Unveränderbar. |
| `identifier` | string | ja | Token-Zielgruppe: eine https-URL (keine Abfrage oder Fragment) oder `urn:<nid>:<nss>`; in kanonischer Form gespeichert. Unveränderbar. Der Namespace `urn:idp:` ist reserviert. |
| `name` | string | ja | 1–128 Zeichen. |
| `access_token_lifetime_s` | integer | nein | 60–86400. Standard 3600 (600 mit `mcp_profile`). |
| `token_format` | string | nein | `jwt` (Standard) oder `opaque`. |
| `rbac_mode` | string | nein | Wie Rollen und Berechtigungen das Token erreichen: `claims` (Standard) fügt `permissions` und `org_permissions` hinzu; `off` fügt keines von beiden hinzu (`org_roles` wird weiterhin hinzugefügt); `enforce` fügt sie hinzu und begrenzt außerdem die gewährten Scopes auf die effektiven Berechtigungen des Benutzers. |
| `allow_offline_access` | boolean | nein | Standard `true`. |
| `require_resource_indicator` | boolean | nein | Standard `false` (`true` mit `mcp_profile`). |
| `dpop_required` | boolean | nein | Standard `false`. |
| `mcp_profile` | boolean | nein | Erfordert eine https `identifier` und `require_resource_indicator: true`. |
| `first_party_access` | string | nein | `all` (Standard) oder `granted_only`. |
| `introspection_applications` | array | nein | Anwendungen (`app_…`), die die Token dieser API introspektieren dürfen, höchstens 50, keine Duplikate. Nur `web` und `m2m` Anwendungen der gleichen Umgebung. Standardmäßig leer. |

### Introspektionsanwendungen

Der Introspektions-Endpunkt (`POST <issuer>/oauth2/introspect`, RFC 7662) antwortet mit `active: true` für ein Zugriffstoken nur an zwei Aufrufer: die Anwendung, der das Token ausgestellt wurde, oder eine Anwendung in der `introspection_applications`-Liste der im Token-`aud` benannten API. Alle anderen Aufrufer erhalten `active: false`, daher können APIs die Token der anderen nicht lesen. Nur vertrauliche Anwendungen (`web`, `m2m`) der gleichen Umgebung können gelistet werden; die Liste wird als Menge gespeichert und in ID-Reihenfolge zurückgegeben. Eine suspendierte Anwendung bleibt auf der Liste, kann aber nicht introspektieren. Die Liste der System-API kann nicht geschrieben werden.

## Antwort

`201 Created` mit einem `ETag`-Header und der API:

```json
{
  "id": "api_01h455vb4pex5vsknk084sn02q",
  "key": "orders",
  "identifier": "https://api.example.com/orders",
  "name": "Orders API",
  "is_system": false,
  "access_token_lifetime_s": 3600,
  "token_format": "jwt",
  "rbac_mode": "claims",
  "allow_offline_access": true,
  "require_resource_indicator": false,
  "dpop_required": false,
  "mcp_profile": false,
  "first_party_access": "all",
  "introspection_applications": ["app_01h455vb4pex5vsknk084sn02r"],
  "created_at": "2026-10-05T12:00:00Z",
  "updated_at": "2026-10-05T12:00:00Z"
}
```

## Fehler

| HTTP | Code | Wann |
|---|---|---|
| 400 | `unknown_field` | Der Body enthält ein Feld, das oben nicht aufgeführt ist (z. B. `id` oder `is_system`). |
| 409 | `conflict` | Eine API mit diesem `key` oder `identifier` existiert bereits. |
| 415 | `unsupported_media_type` | Der Content-Type ist nicht `application/json`. |
| 422 | `validation_failed` | Ein Feld fehlt oder ist ungültig (`errors[].pointer`, z. B. `/identifier`). |
| 422 | `validation_failed` | Ein `introspection_applications`-Element existiert nicht in dieser Umgebung (`unknown_application`), ist nicht vertraulich (`not_confidential`), ist ein Duplikat (`duplicate`) oder ist keine `app_…`-ID (`invalid_value`); der Zeiger benennt den Index in der Anfrage. |
| 403 | `quota_exceeded` | Die Umgebung hat ihre technische API-Grenze erreicht. |
| 403 | `insufficient_permissions` | Die Anmeldedaten haben nicht die erforderliche `apis:write`-Berechtigung. |

## Beispiel

```bash
curl -X POST https://api.eu.keenauth.com/v1/apis \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"key":"orders","identifier":"https://api.example.com/orders","name":"Orders API"}'
```
