
# Crear una API

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

Crea una API en el entorno. `key`, `identifier` y `name` son requeridos; los campos opcionales omitidos toman sus valores por defecto. El identificador es único en el entorno (una API eliminada mantiene su identificador reservado durante 90 días). La respuesta incluye un `ETag` con la versión actual. Envía un encabezado `Idempotency-Key` para hacer los reintentos seguros.

## Solicitud

Cuerpo `application/json`. Los campos desconocidos son rechazados.

| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
| `key` | string | sí | Clave de configuración estable, `^[a-z0-9_-]{1,64}$`. Inmutable. |
| `identifier` | string | sí | Audiencia de token: una URL https (sin consulta ni fragmento) o `urn:<nid>:<nss>`; almacenada en forma canónica. Inmutable. El espacio de nombres `urn:idp:` está reservado. |
| `name` | string | sí | 1-128 caracteres. |
| `access_token_lifetime_s` | integer | no | 60-86400. Por defecto 3600 (600 con `mcp_profile`). |
| `token_format` | string | no | `jwt` (por defecto) u `opaque`. |
| `rbac_mode` | string | no | Cómo llegan los roles y permisos al token: `claims` (por defecto) añade `permissions` y `org_permissions`; `off` no añade ninguno (`org_roles` se sigue añadiendo); `enforce` los añade y además limita los scopes concedidos a los permisos efectivos del usuario. |
| `allow_offline_access` | boolean | no | Por defecto `true`. |
| `require_resource_indicator` | boolean | no | Por defecto `false` (`true` con `mcp_profile`). |
| `dpop_required` | boolean | no | Por defecto `false`. |
| `mcp_profile` | boolean | no | Requiere un `identifier` https y `require_resource_indicator: true`. |
| `first_party_access` | string | no | `all` (por defecto) u `granted_only`. |
| `introspection_applications` | array | no | Aplicaciones (`app_…`) que pueden introspeccionar los tokens de esta API, máximo 50, sin duplicados. Solo aplicaciones `web` y `m2m` del mismo entorno. Vacío por defecto. |

### Aplicaciones de introspeción

El punto de conexión de introspeción (`POST <issuer>/oauth2/introspect`, RFC 7662) responde con `active: true` para un token de acceso solo a dos llamadores: la aplicación a la que se emitió el token, o una aplicación en la lista `introspection_applications` de la API nombrada en el `aud` del token. Todos los demás llamadores reciben `active: false`, por lo que las API no pueden leer los tokens entre sí. Solo las aplicaciones confidenciales (`web`, `m2m`) del mismo entorno pueden listarse; la lista se almacena como un conjunto y se devuelve en orden de ID. Una aplicación suspendida permanece en la lista pero no puede introspeccionar. La lista de la API del sistema no se puede escribir.

## Respuesta

`201 Created` con un encabezado `ETag` y la 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"
}
```

## Errores

| HTTP | código | Cuándo |
|---|---|---|
| 400 | `unknown_field` | El cuerpo contiene un campo que no está listado arriba (por ejemplo `id` o `is_system`). |
| 409 | `conflict` | Una API con esta `key` o `identifier` ya existe. |
| 415 | `unsupported_media_type` | El tipo de contenido no es `application/json`. |
| 422 | `validation_failed` | Un campo está faltando o es inválido (`errors[].pointer`, por ejemplo `/identifier`). |
| 422 | `validation_failed` | Un elemento `introspection_applications` no existe en este entorno (`unknown_application`), no es confidencial (`not_confidential`), es un duplicado (`duplicate`) o no es un ID `app_…` (`invalid_value`); el puntero nombra el índice en la solicitud. |
| 403 | `quota_exceeded` | El entorno alcanzó su límite técnico de API. |
| 403 | `insufficient_permissions` | La credencial carece de `apis:write`. |

## Ejemplo

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