# Autenticación

La API de Meteor se autentica con una **API key** secreta. Cada request lleva la key en el header `Authorization`, y la key define **a qué workspace** pertenece y **qué puede hacer** (sus scopes).

## Formato de la key

Todas las keys empiezan con un prefijo que indica su tipo:

- `met_live_…` — key de producción. Ejecuta acciones reales y consume Energía.
- `met_test_…` — key de **modo test**: runs sin costo, ideal para desarrollar. Los efectos externos (mandar WhatsApp, ejecutar integraciones) quedan bloqueados. **No es un sandbox aislado:** los scopes no restringidos siguen leyendo y escribiendo el workspace real.

> La key es un secreto de servidor. Nunca la pongas en el navegador, en una app móvil, ni la subas al control de versiones. Si se filtra, revócala desde el panel y genera una nueva.

## Crear una key

Desde tu panel de Meteor: **Ajustes → Desarrolladores → Crear API key**. Eliges los scopes que necesita y el entorno (live o test). La key se muestra **una sola vez** — guárdala apenas la creas.

Puedes crear y administrar keys aunque el workspace todavía no tenga plan. Para **usarlas**, el workspace necesita un plan mensual vigente de Meteor; un trial o un plan bonificado también habilitan la operación. Los Tech Partners aprobados pueden recibir un plan especial de USD 0 de cargo fijo mensual, habilitado únicamente por Meteor, con la Energía a tarifa premium.

## Usar la key

Guárdala en una variable de entorno, nunca hardcodeada:

```bash
export MET_API_KEY=met_live_tu_key_aqui
```

Con el SDK:

```ts
import Met from '@meteor.ia/sdk';

const met = new Met(process.env.MET_API_KEY, { workspaceId: 7 });
```
```python
import os
from meteor_ia import Met

met = Met(os.environ["MET_API_KEY"], workspace_id=7)
```

Con `curl`, la key va como Bearer token:

```bash
curl https://api.met.meteor.com.co/api/v1/workspaces/7/runs \
  -H "Authorization: Bearer $MET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input":"Hola"}'
```

## Saber con qué identidad estás entrando

Una key no dice de quién es con solo mirarla. Para preguntárselo al servidor está `GET /me`: devuelve la identidad que la API le reconoce a la key con la que llamas — quién es el dueño, qué workspace opera, con qué scopes y en qué entorno.

```bash
curl https://api.met.meteor.com.co/api/v1/me \
  -H "Authorization: Bearer $MET_API_KEY"
```

```ts
const yo = await met.me.retrieve();
console.log(yo.workspace?.id, yo.api_key.scopes, yo.livemode);
```
```python
yo = met.me.retrieve()
print(yo["workspace"]["id"], yo["api_key"]["scopes"], yo["livemode"])
```

La respuesta, tal como llega por el cable. Toda respuesta correcta de la API viaja
dentro de un sobre y el recurso está en `data`:

```json
{
  "success": true,
  "data": {
    "object": "identity",
    "livemode": true,
    "api_key": {
      "id": "key_01HXYZ",
      "object": "api_key",
      "owner_type": "workspace",
      "env": "live",
      "scopes": ["items:read", "runs:execute", "runs:read"],
      "rate_limit_rpm": 120,
      "monthly_quota": null
    },
    "workspace": { "object": "workspace", "id": 7, "slug": "acme" },
    "partner": null
  }
}
```

Los SDK oficiales te entregan directamente el contenido de `data`, por eso en los
ejemplos de arriba `yo` es el objeto de adentro. Si llamas con `curl` o con un
cliente propio, lee `.data`. Un cliente generado del contrato ya lo sabe: el
OpenAPI declara el sobre. Ver [Errores e idempotencia](errores-e-idempotencia.html).

Qué hacer con cada campo:

| Campo | Para qué te sirve |
|---|---|
| `api_key.id` | Identifica la fila en el panel y en los logs de request. **No es el secreto** y no autentica nada: puedes pegarlo en un ticket de soporte sin riesgo. |
| `api_key.owner_type` | `workspace` si la key es de un cliente, `partner` si es de un Tech Partner. |
| `api_key.scopes` | Lo que la key puede hacer, según el servidor. Si una llamada te responde `403 missing_scope`, esta es la lista contra la cual compararla. |
| `livemode` / `api_key.env` | Si lo que hagas tiene efecto real. Míralo aquí y no en el prefijo de la key: es el mismo criterio que aplica el servidor. |
| `workspace.id` | El workspace que este request opera. Con una key de workspace es siempre el suyo; con una de partner, el cliente atribuido que estás operando. |
| `partner.id` | El partner dueño de la key, o `null`. |
| `rate_limit_rpm` · `monthly_quota` | Los topes que se te aplican de verdad: el más estricto entre el de la key y el de tu plan. `null` es sin tope. Son los números detrás de un `429`. |

El endpoint **nunca devuelve el secreto de la key**, ni su prefijo, ni sus últimos caracteres, ni su longitud. Si perdiste la key, no la recuperas aquí: se crea una nueva en el panel.

Hoy `GET /me` requiere el scope `runs:read`. Si tu key no lo tiene, responde `403 missing_scope` — la key es válida, simplemente no alcanza este endpoint.

### Desde la terminal

`met whoami` imprime lo mismo, y con `--save-workspace` guarda en tu config local el workspace que el servidor resolvió, para que no tengas que pasar `--workspace` en cada comando:

```bash
met whoami
met whoami --save-workspace
met whoami --json
```

Si tu config local apunta a un workspace y la key opera otro, `met whoami` te lo dice: es el error que más tiempo hace perder, porque todo responde `200` y los datos simplemente no son los que esperabas.

## Scopes

Una key solo puede hacer aquello para lo que tiene **scope**. Los scopes siguen el patrón `dominio:acción`, donde `:write` cubre crear, editar y borrar (no existe un `:update` aparte). Pide solo los que tu integración necesita: es más seguro.

Estas tablas salen del catálogo de scopes de la API y del contrato OpenAPI. La columna **Endpoints** dice cuántas operaciones REST piden ese scope hoy.

Un `—` en **Endpoints** significa que hoy ninguna operación REST pide ese scope. Sigue siendo válido: una key que ya lo tenga no deja de funcionar.

Ojo con `conversions:read`: sin endpoints REST, pero **con eventos de webhook detrás**. Para suscribir un endpoint a esos eventos, el scope hace falta.

`rag:read`, `rag:write` y `sites:read` ya **no se ofrecen** al emitir una key nueva ni al registrar una app OAuth. Las keys que los tengan guardados siguen igual; simplemente no habilitan ninguna llamada.

### Ejecución de Mets

| Scope | Permite | Endpoints |
|---|---|---|
| `agents:read` | Ver Mets | 6 |
| `agents:write` | Crear y configurar Mets | 9 |
| `runs:execute` | Ejecutar Mets — **consume Energía** | 1 |
| `runs:read` | Leer runs e historial | 3 |
| `skills:read` | Ver skills | 9 |
| `skills:manage` | Crear, editar y quitar skills | 11 |
| `functions:read` | Ver Funciones IA (código y tools del Met) | 3 |
| `functions:write` | Crear y editar Funciones IA | 5 |
| `rag:read` | Consultar colecciones indexadas | — |
| `rag:write` | Indexar contenido para consultarlo después | — |
| `flows:write` | Crear, editar, duplicar y archivar flujos | 5 |
| `automations:read` | Ver disparadores | 14 |
| `automations:write` | Crear, editar y borrar disparadores | 7 |
| `automations:execute` | Disparar automatizaciones | 4 |

### Datos

| Scope | Permite | Endpoints |
|---|---|---|
| `collections:read` | Leer colecciones | 13 |
| `collections:write` | Crear, editar y borrar colecciones | 22 |
| `items:read` | Leer ítems | 9 |
| `items:write` | Crear, editar y borrar ítems | 27 |
| `contacts:read` | Leer contactos del CRM | 11 |
| `contacts:write` | Crear, editar y borrar contactos | 16 |
| `tasks:read` | Leer tareas agénticas (los pasos que ejecuta un Met) | 10 |
| `tasks:write` | Crear, editar y borrar tareas | 25 |
| `variables:read` | Leer variables del workspace | 5 |
| `variables:write` | Crear, editar y borrar variables | 6 |
| `files:read` | Leer archivos y mediateca | 2 |
| `files:write` | Subir y borrar archivos | 7 |

### Conversaciones y canales

| Scope | Permite | Endpoints |
|---|---|---|
| `conversations:read` | Leer conversaciones de WhatsApp y del CRM | 7 |
| `handoff:manage` | Piloto automático, asignación y mensaje de operador | 3 |
| `channels:read` | Estado de canales y difusiones | 3 |
| `channels:send` | Enviar por WhatsApp y difusiones | 13 |

### Integraciones y plataforma

| Scope | Permite | Endpoints |
|---|---|---|
| `integrations:read` | Ver integraciones MCP | 3 |
| `integrations:manage` | Activar integraciones y guardar sus credenciales | 3 |
| `integrations:execute` | Ejecutar tools de integración | 4 |
| `mcp:use` | Abrir sesión en el servidor MCP | 2 |
| `webhooks:manage` | Webhooks entrantes y suscripciones a eventos salientes | 18 |
| `events:read` | Feed de actividad del workspace | 4 |
| `reminders:read` | Ver recordatorios | 1 |
| `reminders:write` | Crear, editar y borrar recordatorios | 3 |
| `billing:read` | Plan y consumo (no existe un `billing:write`) | 10 |
| `conversions:read` | Estado y configuración de conversiones, y los eventos `conversion.sent` y `conversion.discarded` | — |
| `conversions:write` | Reportar una conversión a Meta (CAPI) | 2 |
| `sites:read` | Ver sitios web | — |
| `snapshots:read` | Navegar el catálogo de plantillas y ver las propias | 2 |
| `snapshots:install` | Instalar una plantilla en el workspace | 2 |

### Solo para keys de partner

Estos scopes **no se pueden emitir en una key de workspace**: el servidor lo re-verifica en cada request y responde `403`. Ver la [API de Partners](../partners.html).

| Scope | Permite | Endpoints |
|---|---|---|
| `snapshots:publish` | Publicar una plantilla al marketplace | 1 |
| `workspaces:provision` | Crear cuentas de cliente | 3 |
| `workspaces:recharge` | Cargar energía a una cuenta que aprovisionaste | 1 |
| `partner:billing:manage` | El medio de pago del partner, con el que paga las cuentas que gestiona | 2 |
| `partner:clients:read` | Clientes atribuidos | 1 |
| `partner:leads:read` | Ver oportunidades | 4 |
| `partner:leads:write` | Crear, editar, mover y borrar oportunidades | 7 |
| `partner:projects:read` | Ver proyectos de implementación | 5 |
| `partner:projects:write` | Crear y editar proyectos, tareas, hitos y avance de fase | 10 |
| `partner:commissions:read` | Comisiones (siempre solo lectura) | 1 |
| `partner:payouts:read` | Liquidaciones (siempre solo lectura) | 2 |
| `partner:support:read` | Tickets propios y de los clientes de su cartera | 3 |
| `partner:support:write` | Abrir tickets, responder y cambiar de estado | 3 |

### Restringidos en modo de prueba

Una key `met_test_` no puede ejercer scopes con efecto externo real: `integrations:execute`, `channels:send`, `conversions:write`, `workspaces:provision`, `workspaces:recharge`, `partner:billing:manage` y `snapshots:install` responden `403` con `test_mode_restricted`. El resto de la superficie funciona igual, incluido el CRUD autorizado: `items:write`, por ejemplo, modifica ítems reales del workspace.

El modo test separa la atribución de ejecución (`livemode: false`) y el costo de los runs; **no crea una copia ni revierte datos**. Para pruebas descartables, usa un workspace dedicado y una key de test de ese workspace.

Si una key intenta algo fuera de sus scopes, la API responde `403` con el código `missing_scope` (el campo `param` trae el scope faltante). No reintentes: pide al dueño de la key que amplíe los scopes.

## Revocar

Una key revocada deja de funcionar en **menos de 60 segundos**. Revoca y rota keys ante cualquier sospecha de filtración, y usa keys distintas por entorno (una para test, otra para producción).

## Apps que operan varios workspaces

Si construyes una app que deben conectar otros workspaces, no les pidas una API key. Registra una **app OAuth** en tu panel: **Ajustes → Desarrolladores → Apps OAuth**. El usuario ve los scopes y autoriza (o revoca) el acceso de tu app.

Sigue la guía de [OAuth para apps de terceros](oauth-apps.html): usa Authorization Code con PKCE S256, callbacks exactos y refresh tokens rotativos.
