
# Créer une API

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

Crée une API dans l'environnement. `key`, `identifier` et `name` sont obligatoires ; les champs optionnels omis prennent leurs valeurs par défaut. L'identifiant est unique dans l'environnement (une API supprimée conserve son identifiant réservé pendant 90 jours). La réponse porte une entête `ETag` avec la version actuelle. Envoyez une entête `Idempotency-Key` pour rendre les retentatives sûres.

## Demande

Corps `application/json`. Les champs inconnus sont rejetés.

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `key` | string | oui | Clé de configuration stable, `^[a-z0-9_-]{1,64}$`. Immuable. |
| `identifier` | string | oui | Public cible du jeton : une URL https (sans requête ni fragment) ou `urn:<nid>:<nss>` ; stockée sous forme canonique. Immuable. L'espace de noms `urn:idp:` est réservé. |
| `name` | string | oui | 1-128 caractères. |
| `access_token_lifetime_s` | integer | non | 60-86400. Par défaut 3600 (600 avec `mcp_profile`). |
| `token_format` | string | non | `jwt` (par défaut) ou `opaque`. |
| `rbac_mode` | string | non | Mode par lequel les rôles et permissions atteignent le jeton : `claims` (par défaut) ajoute `permissions` et `org_permissions` ; `off` n'ajoute aucun des deux (`org_roles` est toujours ajouté) ; `enforce` les ajoute et limite en plus les scopes accordés aux permissions effectives de l'utilisateur. |
| `allow_offline_access` | boolean | non | Par défaut `true`. |
| `require_resource_indicator` | boolean | non | Par défaut `false` (`true` avec `mcp_profile`). |
| `dpop_required` | boolean | non | Par défaut `false`. |
| `mcp_profile` | boolean | non | Nécessite un `identifier` https et `require_resource_indicator: true`. |
| `first_party_access` | string | non | `all` (par défaut) ou `granted_only`. |
| `introspection_applications` | array | non | Applications (`app_…`) pouvant introspect les jetons de cette API, au maximum 50, pas de doublons. Uniquement des applications `web` et `m2m` du même environnement. Vide par défaut. |

### Applications d'introspection

Le point de terminaison d'introspection (`POST <issuer>/oauth2/introspect`, RFC 7662) répond `active: true` pour un jeton d'accès uniquement à deux appelants : l'application à laquelle le jeton a été émis, ou une application dans la liste `introspection_applications` de l'API nommée dans le `aud` du jeton. Tous les autres appelants reçoivent `active: false`, de sorte que les API ne peuvent pas lire les jetons les uns des autres. Seules les applications confidentielles (`web`, `m2m`) du même environnement peuvent être répertoriées ; la liste est stockée sous forme d'ensemble et renvoyée dans l'ordre des identifiants. Une application suspendue reste sur la liste mais ne peut pas introspect. La liste de l'API système ne peut pas être écrite.

## Réponse

`201 Created` avec une entête `ETag` et l'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"
}
```

## Erreurs

| HTTP | code | Quand |
|---|---|---|
| 400 | `unknown_field` | Le corps contient un champ qui n'est pas listé ci-dessus (par exemple `id` ou `is_system`). |
| 409 | `conflict` | Une API avec cette `key` ou cet `identifier` existe déjà. |
| 415 | `unsupported_media_type` | Le type de contenu n'est pas `application/json`. |
| 422 | `validation_failed` | Un champ manque ou est invalide (`errors[].pointer`, par exemple `/identifier`). |
| 422 | `validation_failed` | Un élément `introspection_applications` n'existe pas dans cet environnement (`unknown_application`), n'est pas confidentiel (`not_confidential`), est un doublon (`duplicate`) ou n'est pas un identifiant `app_…` (`invalid_value`) ; le pointeur nomme l'index dans la demande. |
| 403 | `quota_exceeded` | L'environnement a atteint sa limite technique d'API. |
| 403 | `insufficient_permissions` | Le credential manque `apis:write`. |

## Exemple

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