Emisso Connect
Empieza aquí

Autenticación

API keys con permisos explícitos para tu backend, OAuth 2.1 para clientes MCP: cómo se autentica cada llamada al gateway.

Connect tiene dos superficies de entrada, REST (/api/v1/*) y MCP (/mcp), y las dos autentican antes de resolver cualquier otra cosa: el método, el content-type y, sobre todo, la existencia de la tool. Quien llama sin credencial válida no puede distinguir «esa tool no existe» de «existe pero no tienes acceso». En un gateway multitenant, esa asimetría protege el catálogo de cada organización.

API keys

La credencial para llamar desde tu backend es una API key: prefijo connect_sk_, enviada como Authorization: Bearer. Se crean en connect.emisso.ai/api-keys, con nombre, permisos y vencimiento opcional.

El valor completo aparece una sola vez, al crearla. La base guarda su sha256 y un prefijo visible (connect_sk_7Qf3…) para nombrarla en la lista; si la pierdes no hay forma de recuperarla. Rotar es el mismo gesto: la opción «rotar» del dashboard crea un reemplazo con los mismos permisos y te ofrece revocar la anterior justo después de copiar el valor nuevo.

Con la clave en la mano, una llamada autenticada:

curl -X POST https://connect.emisso.ai/api/v1/tools/core.timestamp.now/execute \
  -H "Authorization: Bearer connect_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"input": {"timezone": "America/Santiago"}}'

Respuesta (200):

{
  "data": {
    "iso": "2026-08-07T14:32:11.412Z",
    "unix": 1786113131,
    "timezone": "America/Santiago"
  },
  "meta": {
    "request_id": "req_...",
    "tool_id": "core.timestamp.now",
    "plane": "action",
    "latency_ms": 12,
    "audit_status": "recorded"
  }
}

Toda respuesta trae este envelope {data, meta}, y el meta.request_id identifica la fila que la llamada dejó en la bitácora. Ahí la clave aparece como lo que es: una API key autentica como agente (actor_type: "agent"), y su id viaja aparte en la columna credential_id. La acción de un agente nunca se atribuye al humano que creó la clave.

Scopes

Cada clave lleva scopes, la lista de permisos que puede ejercer. Se otorgan por conector, no por tool: todas las tools de un sistema comparten su scope, y una tool nueva de un conector que ya usas reutiliza el permiso existente en vez de estrenar uno (si lo estrenara, toda clave anterior despertaría con un 403 inexplicable). El vocabulario completo:

ScopeQué permite
sii:readCompras y ventas (RCV), boletas, guías de despacho, boletas de honorarios y el respaldo XML de los documentos emitidos
bci_pyme:readSaldos y movimientos del Banco BCI
banco_security:readSaldos, movimientos, transferencias y nóminas del Banco Security
bice_empresas:readSaldos y movimientos del Banco BICE
bch_empresas:readSaldos, movimientos y cartolas del Banco de Chile
banco_estado:readSaldos y movimientos de BancoEstado
previred:readPlanillas pagadas, cotizaciones por trabajador, deuda previsional y certificados de cotizaciones
indicadores:readUF, dólar, euro, IPC y UTM
core:readDiagnóstico: hora del servidor
echo:readDiagnóstico: eco
conexiones:readVer qué sistemas están conectados, si ya sincronizaron y qué herramientas ofrecen
conexiones:writeEmitir enlaces de un solo uso donde una persona entrega sus credenciales reales del banco o del SII
notta:readVer, listar y descargar las facturas y notas ya emitidas
notta:writeEmitir facturas y notas ante el SII a nombre de la empresa de esta conexión, y reenviar por correo un documento ya emitido. Un documento emitido no se borra: solo se corrige con una nota de crédito

Tres reglas gobiernan la lista:

  • Todo es explícito. El formulario emite exactamente los permisos que marcaste; ni * ni comodines por conector (sii:*) se aceptan. Puedes otorgar hoy el scope de un sistema que aún no conectaste: queda inerte hasta que la conexión exista, y conectar el sistema después no obliga a re-acuñar la clave.
  • Vacío significa cero autoridad, nunca «todos los permisos». Por eso una clave sin ningún permiso marcado no se puede crear; si construyes sobre este modelo, esa es la trampa a evitar.
  • Lo que no termina en :read es sensible. Hoy son dos: conexiones:write, que emite enlaces donde una persona entrega credenciales reales, y notta:write, que emite facturas y notas ante el SII a nombre de la empresa de esta conexión y reenvía las ya emitidas al correo que se le indique (un documento emitido no se borra, solo se corrige con una nota de crédito). El dashboard aísla a los dos, cada uno en su propio contenedor de advertencia.

Una llamada que exige un scope que la clave no tiene responde 403 scope_not_granted, con su suggested_fix en el envelope (catálogo de errores).

scope no es alcance

scope es el permiso de ejecución («¿puedes llamar esto?»). alcance es un módulo de datos del plano read («¿qué subconjunto sincroniza esta conexión?»). Son ejes independientes: sii:read es un scope; rcv y boletas son alcances. La distinción completa está en Los dos planos.

OAuth 2.1 para clientes MCP

Un cliente MCP interactivo (Claude, ChatGPT, un agente de terceros) no maneja una API key estática, así que /mcp es además un OAuth 2.1 Resource Server y el enrolamiento ocurre solo. Al agregar https://connect.emisso.ai/mcp en el cliente:

  1. El cliente descubre los metadatos del recurso protegido (/.well-known/oauth-protected-resource) y, desde ahí, el authorization server.
  2. Se registra en caliente en /register (Dynamic Client Registration): sin coordinación previa y sin client secret, porque solo se admiten clientes públicos.
  3. Abre /authorize con PKCE S256 obligatorio; una persona inicia sesión, elige la organización y aprueba.
  4. Canjea el código en /token y recibe un access token connect_at_ y un refresh token connect_rt_.

El parámetro resource del flujo (RFC 8707) liga el token a la audiencia de este gateway, y cada llamada la verifica: el token no sirve contra ningún otro recurso. Y autentica como usuario: la bitácora registra actor_type: "user" con la identidad de quien aprobó, en vez de un agente anónimo.

El challenge

Una llamada a /mcp sin bearer no revela nada del catálogo; responde el challenge estándar:

curl -i -X POST https://connect.emisso.ai/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'

Respuesta (cabeceras recortadas):

HTTP/2 401
www-authenticate: Bearer resource_metadata="https://connect.emisso.ai/.well-known/oauth-protected-resource"

{"jsonrpc":"2.0","id":null,"error":{"code":-32001,"message":"unauthorized"}}

Ese header le dice al cliente dónde iniciar el flujo OAuth. El mismo challenge protege /api/v1/*. La falla queda registrada en security_events con el prefijo de la credencial; a la bitácora no llega, porque una falla de autenticación no tiene organización que la reciba.

Discovery y challenge no se mezclan

/.well-known/oauth-protected-resource (RFC 9728) es un documento de descubrimiento público que siempre responde 200, sin autenticación. El challenge 401 + WWW-Authenticate vive solo en /mcp y /api/v1/*. Un cliente que recibiera un 401 en el endpoint de metadatos no podría completar el flujo nunca.

Cuándo usar cuál

Las dos superficies aceptan las dos credenciales; la elección real es quién custodia el secreto.

API key (connect_sk_)OAuth 2.1 (connect_at_ / connect_rt_)
Caso naturalTu backend, scripts y agentes propios, sobre REST o MCPUn cliente MCP interactivo que una persona conecta
Cómo naceLa creas tú en el dashboardEl cliente se registra solo (DCR) y una persona aprueba
AutoridadLos scopes explícitos que marcasteLos permisos que pide el cliente, acotados por tu rol en la organización elegida al aprobar
Actor en la bitácoraagentuser
VidaHasta revocarla, o el vencimiento que le pongasEl access token vence y el cliente lo renueva con el refresh token

Próximos pasos

  • Tu primera llamada: el quickstart con un dato real, sin conexión de por medio.
  • Conectar un sistema: del enlace de conexión a la primera sincronización.
  • MCP: las dos meta-tools y el recorrido que hace un agente.

On this page