@emisso/connect
v0.1.0
Published
SDK TypeScript de Emisso Connect: un solo endpoint tipado hacia los sistemas chilenos (SII, bancos de empresa, Previred, emisión de DTE, indicadores del Banco Central).
Maintainers
Readme
@emisso/connect
SDK TypeScript de Emisso Connect: un solo endpoint tipado entre tu software (o tus agentes) y los sistemas chilenos. SII, bancos de empresa, Previred, emisión de DTE e indicadores del Banco Central, todos detrás del mismo catálogo de tools.
Instalación
npm install @emisso/connectFunciona en Node 20 o superior (usa el fetch nativo) y no agrega dependencias de runtime.
Para empezar
import { createClient } from "@emisso/connect";
const connect = createClient({ apiKey: "connect_sk_..." });
// La hora del gateway, tipada de punta a punta.
const ahora = await connect.tools.core.timestamp.now({ timezone: "America/Santiago" });
// → { iso: string; unix: number; timezone: string }
// El valor vigente de la UF. No requiere conexión ni permisos extra.
const uf = await connect.tools.indicadores.valor.actual({ codigo: "UF" });
// → { codigo: string; valor: number; fecha: string; unidad: string; antiguedadDias: number }La API key (connect_sk_...) se crea en el dashboard de Emisso Connect, sección API keys.
Conexiones
Los sistemas conectables (SII, bancos, Previred, Notta) se usan a través de una conexión: el vínculo entre tu organización y ese sistema, con id conn_.... En toda tool de un sistema conectable, connectionId es obligatorio. La conexión es la empresa, y por eso no hay elección implícita, ni siquiera cuando existe una sola: una llamada sin connectionId devuelve validation_error antes de tocar el sistema externo.
const { conexiones } = await connect.tools.conexiones.estado.consultar({ sistema: "sii" });
const conexionId = conexiones[0]?.id;
if (!conexionId) throw new Error("Todavía no hay una conexión al SII: créala desde el dashboard.");
const rcv = await connect.tools.sii.rcv.consultar(
{ periodo: "2026-07", perspectiva: "ventas" },
{ connectionId },
);Leer datos de un banco o del SII son dos pasos. <sistema>.conexion.sincronizar abre una sesión real contra el sistema y persiste lo que trae (puede tardar cerca de un minuto). <recurso>.consultar lee lo ya persistido y nunca pregunta en vivo. Si una consulta vuelve vacía, revisa datosListos en conexiones.estado.consultar antes de concluir que no hay datos.
createClient(options)
| Opción | Tipo | Default | Notas |
|---|---|---|---|
| apiKey | string | ninguno | Tu clave connect_sk_.... Entrega esta opción o apiKeyProvider. |
| apiKeyProvider | () => string \| Promise<string> | ninguno | Fuente asíncrona de la clave (por ejemplo, un vault). |
| baseUrl | string | https://connect.emisso.ai/api/v1 | Conserva el subpath; recorta el slash final. |
| maxAttempts | number | 3 | Presupuesto de reintentos. Aplica solo a GET. |
| onUnauthorized | () => Promise<string \| null> | ninguno | Costura para OAuth: ante un 401 se llama una vez, y con el token nuevo se reintenta la petición (cualquier verbo). |
| fetch | typeof fetch | globalThis.fetch | Reemplaza la implementación de fetch. |
| clientTraceId | string | ninguno | Header x-client-trace-id por defecto para cada llamada. El gateway lo persiste en la bitácora como dato no confiable, separado del request_id: sirve para que un agente que atiende a varias personas diga quién pidió qué. |
Accessor tipado
// connect.tools.<sistema>.<recurso>.<verbo>(input?, opts?)
const eco = await connect.tools.echo.message.reflect({ text: "hola" });
// .withResponse(...) → el envelope completo { data, requestId, meta, pagination }
const res = await connect.tools.core.timestamp.now.withResponse();
console.log(res.requestId, res.data);
// Escape genérico por id, sin tipos por tool.
const data = await connect.tools.execute("core.timestamp.now", {});opts: CallOptions = { signal?, connectionId?, clientTraceId? }. El connectionId (un id conn_...) viaja como header X-Connect-Connection; el clientTraceId por llamada pisa el del cliente.
Errores
ConnectError se lanza ante una falla del gateway (una respuesta HTTP no ok) y ante errores de configuración del cliente (falta fetch, falta apiKey/apiKeyProvider). Los de configuración se lanzan de forma síncrona desde createClient(...), antes de cualquier llamada, así que el try/catch de abajo (alrededor de una tool) no los cubre:
import { ConnectError } from "@emisso/connect";
try {
await connect.tools.core.timestamp.now();
} catch (e) {
if (e instanceof ConnectError) {
console.error(e.code, e.status, e.requestId, e.suggestedFix);
}
}code es un valor del catálogo compartido de errores de Emisso Connect, o "transport_error" cuando el cuerpo del error no trae un error.code interpretable. Todo error del gateway incluye un requestId y un suggestedFix accionable, pensado también para agentes. Una falla de red que nunca llega al gateway (DNS, conexión rechazada, petición abortada) se propaga como el rechazo nativo de fetch, sin envolver en ConnectError.
Reintentos
Los GET se reintentan hasta maxAttempts ante errores marcados como retryable (respetando Retry-After; si no viene, backoff exponencial). Los POST nunca se reintentan solos: una acción regulada no se puede duplicar. La única excepción es el 401 → refresh → reintento vía onUnauthorized, para cualquier verbo y fuera del presupuesto de reintentos. Cada petición viaja con un header Idempotency-Key; en las tools destructivas (como la emisión de DTE) el gateway lo usa para deduplicar de verdad.
Documentación
La referencia completa del catálogo (cada tool con sus schemas de entrada y salida), los conceptos y las guías de conexión viven en connect.emisso.ai/docs. El mismo catálogo se sirve también por REST y como servidor MCP; este SDK es la proyección TypeScript.
