# Autenticación con clave de acceso

Cómo autentica la API externa a tu aplicación con la cabecera X-Api-Key, qué comprueba en cada petición y qué error devuelve cuando algo falla.

La API externa (`https://os.utilia.ai/api/external/v1/...`) reconoce a tu aplicación por su **clave de API**, que envías en la cabecera `X-Api-Key` en cada petición. No hay sesión, ni cookies, ni token que renovar: la clave basta, y por eso solo debe vivir en tu servidor.

```http
GET /api/external/v1/users HTTP/1.1
Host: os.utilia.ai
X-Api-Key: utl_tu_clave
```

## De dónde sale la clave

Cada aplicación externa tiene **una clave propia**, que se genera al crearla con **Crear la aplicación**, desde el botón **Nueva aplicación** de **Ajustes › Creadas por tu empresa**. La clave empieza por `utl_` y solo se muestra una vez, en la pantalla «Aplicación creada». UTILIA OS guarda únicamente su huella, así que nadie puede volver a enseñártela.

Si la pierdes o sospechas que se ha filtrado, renuévala desde la ficha de la aplicación, en **Ajustes**. Puedes elegir cuántos días sigue valiendo la anterior (1, 7 o 30) para desplegar la nueva sin cortes, o que deje de valer en el acto, que es lo que toca si se ha filtrado. Mientras la anterior sigue valiendo, las peticiones que la usan funcionan y su respuesta lleva la cabecera `X-Utilia-Api-Key-Expires` con la fecha en que dejará de valer: si la ves en tus registros, algún servidor tuyo todavía no tiene la nueva. Pueden renovarla quienes tienen el permiso **Editar aplicaciones externas** o **Gestionar aplicaciones externas**.

## La persona responsable

Cada aplicación tiene una **persona responsable**, que se ve y se cambia en la ficha. Lo que la aplicación crea o cambia por la API queda a su nombre, y tiene que estar activa: si deja de estarlo y nadie la sustituye, UTILIA OS rechaza **todas** las peticiones de la aplicación con `403 APP_RESPONSIBLE_INACTIVE`.

Lo que la aplicación puede hacer lo deciden, sobre todo, sus **permisos de la API** (los scopes que le concede la organización), no los permisos de esa persona. Los de la persona solo cuentan en estos casos:

- **Rectificativas**: emitir una exige además que la persona responsable pueda rectificar esa factura. Si no, `403 APP_RESPONSIBLE_PERMISSION_DENIED`.
- **Suscripciones y cobros programados**: se gestionan con el motor de Finanzas en nombre de la persona responsable. Si le han retirado el permiso sobre ellos, `403 APP_RESPONSIBLE_PERMISSION_DENIED`.
- **Escrituras del chat**: crear un canal exige que la persona responsable pueda crear canales, y escribir en uno, que quien concedió el canal a la aplicación conserve ese permiso (`403 APP_NOT_AUTHORIZED_IN_CHANNEL`).

En el resto (clientes, contactos, leads, comentarios y firmas de presupuesto, facturas, cobros, archivos, usuarios externos y tickets) basta el permiso de la API. Los datos del CRM y de los presupuestos alcanzan a toda la organización; los usuarios externos, las facturas, los cobros, los archivos, los tickets y los errores, solo a los de la propia aplicación.

## Qué se comprueba en cada petición

Antes de llegar a la operación, cada petición pasa estos controles, en este orden. El primero que falla corta la petición con su código:

**Controles de cada petición a la API externa**

1. **¿Llega la clave en X-Api-Key?** (Cabecera X-Api-Key): Antes de nada, la clave sola en la dirección se rechaza con 401 API_KEY_IN_URL_NOT_ALLOWED. Solo en eventos en tiempo real vale además un pase de conexión en `streamToken`. Si no: 401 API_KEY_REQUIRED, API_KEY_WRONG_HEADER o API_KEY_WRONG_KIND.
2. **¿Es válida y de una aplicación?**: Existe, está activa, no ha caducado y pertenece a una aplicación externa. Durante una renovación con transición vale también la anterior. Si no: 401 API_KEY_INVALID o API_KEY_NOT_LINKED.
3. **¿Aplicación y responsable activos?**: La aplicación está activa y su persona responsable sigue activa en la organización. Si no: 403 APP_DISABLED o APP_RESPONSIBLE_INACTIVE.
4. **¿Le queda cupo?**: Peticiones por hora de la clave; 1.000 en una aplicación nueva. Si no: 429 RATE_LIMIT_EXCEEDED, con Retry-After.
5. **¿El origen está autorizado?**: Solo si la petición llega desde una web y la aplicación tiene dominios autorizados. Si no: 403 ORIGIN_NOT_ALLOWED.
6. **¿Se puede usar desde una página?**: Desde un navegador, solo los permisos del widget. Ver Claves dentro de una página web. Si no: 403 SCOPE_NOT_ALLOWED_FROM_BROWSER.
7. **¿Está en el plan?** (Plan de la organización): El aplicativo y la capacidad de la operación. Si no: 403 MODULE_NOT_ENABLED, CAPABILITY_NOT_ENABLED o FEATURE_COMING_SOON.
8. **¿Tiene el permiso?** (Permisos de la aplicación): El permiso de la API que exige la operación, concedido a la aplicación. Si no: 403 INSUFFICIENT_SCOPE, con el permiso en requiredScopes.

_Los seis primeros comprueban la clave y la aplicación; los dos últimos, el plan de la organización y el permiso de la aplicación, en ese orden._

Si la organización está suspendida o dada de baja, las consultas siguen funcionando y las escrituras responden `403 TENANT_SUSPENDED` o `TENANT_CANCELLED`. Toda respuesta de error trae además `fixedBy` (quién lo arregla: tú, la organización o UTILIA OS) y `docsUrl`, el apartado de la documentación que lo explica. Cada código está en [Errores, identificador de petición y reintentos](https://os.utilia.ai/docs/developers/getting-started/errors-and-retries). La organización con la que trabaja la petición sale siempre de la aplicación dueña de la clave, nunca de lo que envíes en el cuerpo.

Para saber qué le pasa a tu clave sin adivinarlo por los errores, `GET /api/external/v1/diagnostics` devuelve qué puede y qué no puede hacer tu aplicación y por qué. Cómo se usa está en [Probar tu integración](https://os.utilia.ai/docs/developers/getting-started/testing-quickstart).

Los límites de uso, los orígenes permitidos y los permisos que admite una clave que va en una página se explican en [Límites de uso y CORS](https://os.utilia.ai/docs/developers/getting-started/rate-limits-and-cors).

## Eventos en tiempo real: un pase en lugar de la clave

`EventSource`, la herramienta del navegador para recibir eventos en tiempo real, no permite añadir cabeceras. Para no poner la clave en la dirección, tu servidor pide antes un **pase de conexión** con la cabecera `X-Api-Key` (`POST /api/external/v1/tickets/stream-token`) y la conexión se abre con el pase en el parámetro `streamToken`:

```javascript
// El pase lo pide tu servidor; aquí solo viaja el pase, nunca la clave.
const fuente = new EventSource(
  `https://os.utilia.ai/api/external/v1/tickets/ai-suggest/${jobId}/stream?streamToken=${pase}`,
);
```

El pase vale cinco minutos y una sola conexión, y va atado a la conexión, al trabajo o al usuario con el que se pidió. Cómo pedirlo y sus errores está en [Endpoints de tickets](https://os.utilia.ai/docs/developers/api/tickets#pase-de-conexión-sin-la-clave-en-la-dirección). Los SDK oficiales lo resuelven solos a partir de la 4.2.0: el de JavaScript pide el pase por ti y el de Python manda la clave en la cabecera. Qué versión está publicada lo dice [Versiones y compatibilidad](https://os.utilia.ai/docs/developers/sdk/versions).

## La clave nunca va en la dirección

Una dirección queda escrita en registros de servidores, servidores intermedios e historiales, así que la API externa no acepta la clave en ella:

- **Sola en la dirección**, con cualquier nombre de parámetro (`apiKey`, `api_key`, `x-api-key`...) o como valor `utl_…` de otro parámetro, la petición se rechaza con `401 API_KEY_IN_URL_NOT_ALLOWED` antes de comprobar nada más.
- **En la dirección y también en la cabecera `X-Api-Key`**, de momento la petición entra por la cabecera, pero queda constancia y el diagnóstico avisa con el hallazgo [`key-in-url`](https://os.utilia.ai/docs/developers/getting-started/testing#key-in-url). Quítala de la dirección cuanto antes y pide que se renueve la clave, porque la que viajó en la dirección puede estar expuesta.
- **La forma antigua de los eventos en tiempo real**, la clave en `apiKey` en peticiones `GET` con `Accept: text/event-stream`, se sigue admitiendo de momento. No la uses en integraciones nuevas: usa el pase de conexión.

Las dos tolerancias dejarán de admitirse, y entonces esas peticiones responderán también `401 API_KEY_IN_URL_NOT_ALLOWED`.

## Qué credencial abre cada puerta

Además del pase de conexión, UTILIA OS reconoce cuatro credenciales, y cada una solo abre su puerta. Cuál te toca según quién llama lo resuelve [Elegir credencial](https://os.utilia.ai/docs/developers/getting-started/choose-credential).

| Credencial | Cómo viaja | Dónde se crea | Qué abre |
| - | - | - | - |
| Clave de aplicación externa (`utl_…`) | Cabecera `X-Api-Key` | Al dar de alta la aplicación, en Ajustes › Creadas por tu empresa | La API externa (`/api/external/v1/...`), con los permisos de la API concedidos a la aplicación |
| Clave de API de la organización (`utk_…`) | `Authorization: Bearer` | Ajustes › Claves de API, tipo «Acceso a la API» | La API que usa la aplicación (`/api/...`), con los permisos de su persona responsable recortados por la clave |
| Clave para enviar leads (`utl_…`) | Cabecera `X-API-Key` | Ajustes › Claves de API, tipo «Enviar leads» | Solo `POST /api/v1/leads` |
| Token de OAuth | `Authorization: Bearer` | Lo autoriza una persona desde tu aplicación o tu asistente de IA | Lo que esa persona autorice, por ejemplo las herramientas MCP |

Si las mezclas, la puerta no se abre:

- A la API externa, una clave para enviar leads responde `401 API_KEY_NOT_LINKED`; una clave de API de la organización, `401 API_KEY_WRONG_KIND`, vaya en `Authorization` o en `X-Api-Key`; y la clave de tu aplicación en `Authorization: Bearer` en lugar de `X-Api-Key`, `401 API_KEY_WRONG_HEADER`.
- A la API de la aplicación (`/api/...`), la clave de una aplicación externa en `Authorization: Bearer` responde `401 API_KEY_WRONG_KIND`, y un token de sesión ausente, mal formado o caducado, `401 AUTH_TOKEN_INVALID`.
- La API del panel que gestiona las aplicaciones externas (`/api/integrations/apps/...`) se llama con la sesión de una persona y sus permisos. No la abre la clave de una aplicación, y tampoco una clave de API de la organización: lo que tiene que ver con Integraciones queda fuera de cualquier clave (`403` con `API_KEY_PERMISSION_NOT_GRANTED` o `API_KEY_ROUTE_NOT_AVAILABLE`).

Cómo se crea, se usa y se recorta la clave de la organización está en [Claves de API de la organización](https://os.utilia.ai/docs/developers/organization-api-keys/overview); el token de OAuth, en [OAuth: visión general](https://os.utilia.ai/docs/developers/oauth/overview).

## Siguiente paso

Revisa cuántas peticiones admite tu clave y cómo se controla desde qué webs se puede llamar en [Límites de uso y CORS](https://os.utilia.ai/docs/developers/getting-started/rate-limits-and-cors).
