API de control
La referencia REST de todo lo que no es una tool: catálogo, conexiones, sesiones, sincronizaciones y webhooks.
Ejecutar una tool es siempre POST /v1/tools/{id}/execute, y su contrato completo vive en la referencia y en el OpenAPI. Alrededor de eso el gateway expone una superficie de control: qué puedes llamar, qué tienes conectado, en qué va una sincronización y a dónde se entregan los eventos. Esta página documenta esa superficie completa.
Cobertura del OpenAPI
Los contratos OpenAPI publicados describen la ejecución de tools. El contrato completo también incluye la creación de enlaces y las solicitudes de carpeta tributaria puntual. Los demás endpoints de control se documentan en esta página.
Convenciones
La base es https://connect.emisso.ai/api/v1. Salvo GET /v1/health, GET /v1 y GET /v1/openapi.json, todos los endpoints exigen Authorization: Bearer connect_sk_… y responden 401 unauthorized con el challenge WWW-Authenticate si el token falta o no sirve. Si la infraestructura de autenticación está caída, la respuesta es 503 service_unavailable (retryable): una caída nuestra nunca se disfraza de «tu clave está mala».
Reglas que aplican a toda la superficie:
- Todo cuerpo de petición está limitado a 256 KB: por encima responde
413 payload_too_large, y un JSON inválido responde400 malformed_request. - Toda respuesta de éxito envuelve el resultado en
{ "data": … }; todo error viene como{ "error": { "code", "message", "request_id", "suggested_fix" } }con el código del catálogo de errores. - Los
GETde esta página no escriben en la bitácora: no ejecutan ninguna tool. Soloexecuteaudita.
Rutas para conexiones y sincronizaciones
Las rutas de esta tabla son la lista cerrada para listar conexiones, pedir una sincronización y
seguir su estado. En los ejemplos, /v1/... siempre lleva delante https://connect.emisso.ai/api.
| Operación | Método y ruta exactos | Cuerpo |
|---|---|---|
| Listar conexiones accesibles a tu clave | GET /v1/connections | Sin cuerpo |
| Ver el estado de una conexión y sus últimos trabajos | POST /v1/tools/conexiones.estado.consultar/execute | {"input":{"conexionId":"conn_..."}} |
| Pedir uno o varios meses | POST /v1/connections/{conn}/syncs | {"periodos":["2026-08","2026-09"]} |
| Seguir un trabajo recibido | GET /v1/syncs/{id} | Sin cuerpo |
| Ejecutar una tool del catálogo | POST /v1/tools/{id}/execute | {"input":{...}}, según su ficha |
No existe GET /v1/connections/{id}, GET /v1/connections/{id}/estado ni GET /v1/connections/{id}/syncs. El título
«Consultar el estado de las conexiones» corresponde a la tool conexiones.estado.consultar,
llamada por POST. GET /v1/connections no devuelve trabajos; la tool devuelve los cinco
últimos, no todas las corridas. Para seguir cada solicitud conserva sus job_ids y consulta cada
uno con GET /v1/syncs/{id}.
Todas las tools de un sistema conectado exigen X-Connect-Connection: conn_..., incluso con una sola
conexión. Las de plataforma o datos abiertos no lo exigen: conexiones.*, core.*, indicadores.* y
echo.* son ejemplos. Toda ejecución de tool exige además Content-Type: application/json; omitirlo
responde 415 unsupported_media_type.
La tool de plataforma conexiones.estado.consultar recibe el filtro en input.conexionId y no
exige ese header. Requiere el permiso conexiones:read. En /connections/{conn}/syncs, la conexión va en la ruta.
Deshabilitar, habilitar y eliminar una conexión son tools conexiones.conexion.* por
POST /v1/tools/{id}/execute; no tienen rutas REST propias bajo /v1/connections/{id}. Crear
un enlace también es una tool, conexiones.enlace.crear, por la misma ruta de ejecución; además
existe la alternativa POST /v1/connect_sessions documentada en esta página. Los títulos de las
fichas describen operaciones y no definen rutas adicionales.
Descubrimiento
GET /v1/health
Sin credencial. Dice si el gateway está arriba y cuántas tools tiene el registro. El conteo varía con el catálogo.
curl https://connect.emisso.ai/api/v1/healthSalida esperada (el conteo es variable):
data.status = "ok"
data.registry.tools = número actual de tools del registroGET /v1
Sin credencial. El puntero raíz del API; a diferencia del resto, responde sin el envelope data.
curl https://connect.emisso.ai/api/v1{
"data_url": "/api/v1",
"openapi": "/api/v1/openapi.json",
"docs": "https://connect.emisso.ai/docs"
}GET /v1/tools
El catálogo filtrado para tu organización: solo los conectores que tienes habilitados y solo lo que cubren los scopes de tu clave. Dos organizaciones ven listas distintas. Cada entrada trae la ficha completa, incluidos los JSON-Schema de entrada y salida y, cuando la tool lo declara, un example ejecutable:
curl https://connect.emisso.ai/api/v1/tools \
-H "Authorization: Bearer connect_sk_..."{
"data": [
{
"id": "sii.rcv.consultar",
"mcpName": "sii__rcv__consultar",
"connector": "sii", "resource": "rcv", "verb": "consultar",
"title": "Consultar RCV del SII",
"description": "…",
"plane": "action",
"reads": "rcv",
"scope": "sii:read",
"auth": "none",
"sensitive": true,
"version": 4,
"deprecated": false,
"annotations": { "readOnly": true, "destructive": false, "idempotent": true, "openWorld": false, "title": "Consultar RCV del SII" },
"inputSchema": { "…": "draft 2020-12" },
"outputSchema": { "…": "draft 2020-12" },
"example": { "input": { "…": "…" }, "output": { "…": "…" } }
}
],
"pagination": { "cursor": null, "hasMore": false }
}plane dice si la tool persiste; reads aparece solo en las tools de consulta y nombra el alcance que leen. El modelo completo está en sincronizar y consultar.
GET /v1/tools/{id}
La misma ficha, para un id concreto. Este endpoint no filtra por organización: devuelve la ficha de cualquier tool del catálogo, esté o no conectado su sistema, porque descubrir no es ejecutar. Un id inexistente responde 404 tool_not_found.
curl https://connect.emisso.ai/api/v1/tools/sii.rcv.consultar \
-H "Authorization: Bearer connect_sk_..."{ "data": { "id": "sii.rcv.consultar", "…": "la misma ficha de GET /v1/tools" } }GET /v1/connectors
Los conectores de uso general, con su estado de habilitación para tu organización. Los sistemas
conectables (SII y bancos) no aparecen aquí: este endpoint sirve solo el catálogo público. Para
obtener los códigos de conector sin adivinar, incluidos los conectables, usa la tool
conexiones.sistemas.listar, que además dice qué ya
está conectado.
curl https://connect.emisso.ai/api/v1/connectors \
-H "Authorization: Bearer connect_sk_..."{ "data": [{ "code": "core", "name": "Core", "plan": "free", "enabled": true }] }Este endpoint lista solo el catálogo público
/v1/connectors sirve los conectores de uso general (core, echo, indicadores, conexiones). Las fuentes private/tenant como el SII o los bancos no aparecen aquí aunque las tengas conectadas y aunque sus tools sí salgan en /v1/tools. Para ver lo que tu organización tiene conectado, usa /v1/connections o la tool conexiones.estado.consultar.
GET /v1/openapi.json
Sin credencial. El contrato OpenAPI 3.1 del catálogo público, servido por el propio runtime. El contrato del catálogo documentado completo (SII y bancos incluidos) es otro artefacto: /docs/openapi.json.
curl -s https://connect.emisso.ai/api/v1/openapi.json | jq .info{
"title": "Emisso Connect API",
"version": "0.1.0"
}Conexiones
GET /v1/connections
Tus conexiones, cada una la unidad de facturación. De aquí salen los conn_… que necesitas para el header X-Connect-Connection.
curl https://connect.emisso.ai/api/v1/connections \
-H "Authorization: Bearer connect_sk_..."{
"data": [
{
"id": "conn_4ly7hnmw2tigryuxfjkr4",
"connector": "sii",
"display_name": "SII prod",
"empresa_rut": "76123456-0",
"status": "active",
"plan": "paid",
"enabled_scopes": ["sii:read"],
"credential_scheme": "portal_credentials_representante"
}
],
"pagination": { "cursor": null, "hasMore": false }
}empresa_rut es el RUT de la empresa o del titular del acceso, sin puntos y con guion; es null mientras no se conoce, por ejemplo antes de que la persona complete el enlace. credential_scheme es el acceso con el que entró la conexión (los codigo de accesos en conexiones.sistemas.listar), o null si todavía no tiene credencial. En el SII, una conexión con portal_credentials_representante puede pasar a emitir con un enlace activar_emision; una con portal_credentials (clave de la empresa) no emite. Los conectores open y platform nunca aparecen: no hay nada que conectar.
Si eres un agente, prefiere la tool
conexiones.estado.consultar responde lo mismo y además: qué alcances tiene activos cada conexión, en qué van sus sincronizaciones, qué tools puedes invocar sobre ella y datosListos, la respuesta a «¿ya hay algo que leer?». Por ser una tool, se llama igual por MCP que por REST.
Sesiones de conexión
El flujo hosted completo (incrustar el iframe, postMessage, el webhook connect_session.consumed, el modelo de amenaza) vive en el enlace hosted. Aquí, el contrato de los dos endpoints.
POST /v1/connect_sessions
Crea un enlace de un solo uso para que un tercero entregue credenciales de una fuente concreta, sin cuenta y sin acceso al dashboard.
Exige conexiones:write en la API key o el token OAuth, también para elegir el sistema en el
formulario y para reconectar. Sin ese permiso responde 403 scope_not_granted. La variante de
carpeta tributaria puntual exige su permiso específico sii:carpeta_tributaria:generar en lugar del de enlaces.
| Campo | Tipo | Notas |
|---|---|---|
mode | "create" | "reauth" | "activar_emision" | "cambiar_clave_certificado" | create (default) emite una conexión nueva; reauth reemplaza la credencial de una existente y exige connection_id; activar_emision pide la clave del certificado digital para que una conexión del SII con clave de persona pase a emitir, y exige connection_id; cambiar_clave_certificado pide la clave nueva del certificado de una conexión del SII que ya emite, y exige connection_id. |
connector_code | string | Una fuente conectable, por ejemplo sii, bch_empresas o itau_empresas. Los códigos válidos son los codigo que devuelve conexiones.sistemas.listar. El enlace queda fijado a esa fuente. |
credential_scheme | string | Opcional, solo en create y con connector_code. El acceso con el que entra la persona (los codigo de accesos en conexiones.sistemas.listar). Omitido, lo elige ella. |
emitir | boolean | Opcional, solo en create y con connector_code. true exige un acceso que emita y la emisión habilitada para la organización; la persona entregará la clave del certificado digital. Omitido, lo decide ella. |
alcances | string[] | Opcional, solo en create y con connector_code. Los módulos de datos que se habilitan, servidos por el acceso fijado (o por todos, si no fijas uno). Omitido, los elige ella. |
display_name | string | Opcional, solo en create. El nombre de la conexión que nazca del enlace, de 1 a 80 caracteres, sin saltos de línea; se quitan los espacios de los extremos. Lo ves en el panel, la bitácora, los cobros y conexiones.estado.consultar; la persona que conecta no lo ve. Omitido, se llama como su sistema (SII, SII 2). Si con connector_code fijado otra conexión de ese sistema ya usa el nombre, responde 400 validation_error; si el choque aparece después (dos enlaces pendientes con el mismo nombre, o un enlace sin sistema fijado), la conexión nueva lleva el sufijo correlativo (… 2). |
connection_id | string | Obligatorio en reauth, activar_emision y cambiar_clave_certificado. Una conexión de otra organización degrada al mismo 400 validation_error: nunca un 404 que confirme que existe. |
allowed_origins | string[] | Hasta 5 orígenes https exactos, sin path ni comodín. Allowlist del iframe y destino del postMessage. |
redirect_uri | string | https, sin fragmento y, si registraste orígenes, dentro de ese conjunto. |
En activar_emision, connector_code es opcional y solo admite sii; credential_scheme, emitir, alcances y display_name no aplican. Si la conexión no puede pasar a emitir, la respuesta dice por qué: 409 requiere_clave_de_persona (entra con la clave de la empresa; crea una conexión nueva con clave de persona y emitir: true), 409 emision_ya_activa, 428 connection_credential_required (renueva primero la credencial con reauth) o 403 connection_disabled. El flujo completo está en activar la emisión en una conexión existente.
cambiar_clave_certificado tiene la misma forma. Si la conexión no lo admite, responde 409 emision_no_activa (todavía no emite; usa activar_emision), 409 requiere_clave_de_persona, 428 connection_credential_required o 403 connection_disabled. El flujo está en cambiar la clave del certificado.
El esquema es estricto: un campo desconocido responde 400 validation_error. La emisión está limitada a 60 enlaces por hora por organización; superado el tope, la respuesta es 429 rate_limited con Retry-After. El enlace vence a las 24 horas en create, activar_emision y cambiar_clave_certificado, y a la 1 hora en reauth, y admite 5 intentos reales de credencial.
Sin un plan activo la respuesta es 402 subscription_required, y con un pago vencido, 402 billing_past_due: el enlace no se emite. Es el mismo corte de la tool conexiones.enlace.crear. La variante de carpeta tributaria puntual no pasa por él.
curl -X POST https://connect.emisso.ai/api/v1/connect_sessions \
-H "Authorization: Bearer connect_sk_..." \
-H "Content-Type: application/json" \
-d '{
"mode": "create",
"connector_code": "sii",
"display_name": "Comercial Andes SpA",
"allowed_origins": ["https://app.tu-producto.cl"],
"redirect_uri": "https://app.tu-producto.cl/conexiones/listo"
}'Respuesta (201):
{
"data": {
"id": "cs_7f3ab9c1d2e4f5061728",
"mode": "create",
"connector_code": "sii",
"connection_id": null,
"token_prefix": "connect_cs_AbC…",
"allowed_origins": ["https://app.tu-producto.cl"],
"redirect_uri": "https://app.tu-producto.cl/conexiones/listo",
"status": "pending",
"attempts": 0,
"max_attempts": 5,
"expires_at": "2026-08-08T12:00:00.000Z",
"consumed_at": null,
"resulting_connection_id": null,
"created_at": "2026-08-07T12:00:00.000Z",
"credential_scheme": null,
"emitir": null,
"alcances": null,
"display_name": "Comercial Andes SpA",
"url": "https://connect.emisso.ai/c/connect_cs_..."
},
"meta": { "request_id": "req_..." }
}El token aparece una sola vez
El token en claro viaja solo en esta respuesta: la base guarda su sha256 y un prefijo visible para nombrarlo en una lista. Si lo pierdes, emite otra sesión y revoca la anterior.
La carpeta tributaria puntual usa una variante de este mismo endpoint con
el bloque collection y su propio permiso. En esa variante, Idempotency-Key es obligatoria y permite
recuperar el mismo enlace mientras espera autorización. Después de aceptarlo, el enlace no se recupera:
consulta la solicitud existente antes de crear otra.
GET /v1/connect_sessions
Lista las sesiones con su token_prefix, su estado (pending, consumed, revoked, expired) y sus intentos; nunca el token. Acepta ?connection_id=conn_… para acotar a una conexión.
curl "https://connect.emisso.ai/api/v1/connect_sessions?connection_id=conn_..." \
-H "Authorization: Bearer connect_sk_..."{
"data": [
{
"id": "cs_7f3ab9c1d2e4f5061728",
"mode": "reauth",
"connector_code": "sii",
"connection_id": "conn_4ly7hnmw2tigryuxfjkr4",
"token_prefix": "connect_cs_AbC…",
"allowed_origins": [],
"redirect_uri": null,
"status": "consumed",
"attempts": 1,
"max_attempts": 5,
"expires_at": "2026-08-07T13:00:00.000Z",
"consumed_at": "2026-08-07T12:14:09.000Z",
"resulting_connection_id": "conn_4ly7hnmw2tigryuxfjkr4",
"created_at": "2026-08-07T12:00:00.000Z"
}
],
"pagination": { "cursor": null, "hasMore": false }
}Carpeta tributaria puntual
Requiere sii:carpeta_tributaria:generar. La guía de carpeta tributaria
describe la autorización de la empresa, los estados y la retención. Estas son sus rutas de control:
| Método y ruta | Qué devuelve |
|---|---|
POST /v1/connect_sessions con collection | El enlace pendiente y el ID de solicitud, o los IDs y el estado actual al reintentar una solicitud ya aceptada. Requiere Idempotency-Key y habilitación del piloto. |
GET /v1/collection_requests/{id} | Estado, causa y metadatos del documento, cuando existe. |
POST /v1/collection_requests/{id}/cancel | El estado final si ya terminó; de lo contrario, cancela antes de iniciar la emisión cuando no hay trabajo activo. |
GET /v1/collection_documents/{id}/download | URL firmada por cinco minutos, solo con la solicitud ready y el documento dentro de su retención. |
La creación comparte con los enlaces generales el cupo de 60 solicitudes por hora y organización:
429 rate_limited incluye Retry-After. Una carpeta sin habilitación responde
403 collection_not_enabled; sin destinatario configurado, 428 collection_configuration_missing.
Un replay pendiente cuyo enlace venció responde 410 collection_expired; cambiar el contenido con
la misma clave responde 409 idempotency_conflict.
Consultar, cancelar o descargar un recurso ajeno o no disponible responde 404 collection_not_found.
Cancelar durante el trabajo responde 409 connection_busy; no revoca una carpeta emitida por el SII.
La entrega por webhook exige que el plan incluya esa función y un endpoint habilitado con filtros
compatibles. Conserva el ID de solicitud para consultar el resultado aunque no llegue el aviso.
Sincronizaciones
La tool <sistema>.conexion.sincronizar y este endpoint encolan el trabajo: ninguno espera el login
ni devuelve los datos sincronizados en esa llamada. La tool responde 200 con data.estado: "encolado"
y data.jobId; este endpoint responde 202 con data.job_ids, un arreglo para pedir varios períodos.
Guarda los identificadores y consulta su resultado antes de dar los datos por actualizados.
La tool pide un período y sus alcances por llamada. El endpoint de control, sin alcances, toma
todos los habilitados de la conexión. Los webhooks también permiten seguir el resultado si tu plan los incluye.
POST /v1/connections/{conn}/syncs
Exige el mismo permiso que la tool <conector>.conexion.sincronizar de la conexión solicitada;
por ejemplo, sii:read para SII o bci_pyme:read para Bci. Un permiso de otro sistema o
conexiones:write no autoriza el sync. La credencial sin el permiso requerido recibe
403 scope_not_granted antes de leer el cuerpo o encolar trabajo.
| Campo | Tipo | Notas |
|---|---|---|
periodos | string[] | Opcional. Cada uno con forma AAAA-MM, hasta el mes corriente de Chile; sin él se encola ese mes. Máximo 24 períodos por petición. |
alcances | string[] | Opcional. Sin él se toman todos los alcances habilitados de la conexión. Cada uno debe estar habilitado en esa conexión. |
curl -X POST https://connect.emisso.ai/api/v1/connections/conn_.../syncs \
-H "Authorization: Bearer connect_sk_..." \
-H "Content-Type: application/json" \
-d '{"periodos":["2026-08"],"alcances":["rcv"]}'Respuesta (202), un trabajo por período encolado:
{ "data": { "job_ids": ["sjb_..."] }, "meta": { "request_id": "req_..." } }Un mes mal formado o futuro se rechaza con 400 validation_error antes de ocupar un cupo.
En la prueba, SII permite los tres meses más recientes (incluido el mes corriente de Chile); pedir
meses anteriores responde 403 feature_not_in_plan. Pedir guías fuera de su ventana de seis meses, un mes de
Santander que terminó antes de sus últimos 90 días, o sincronizar Previred durante el cierre del 10 al 13 para períodos fuera de los tres meses del alta, responde
400 validation_error. Un sistema sin tool de sincronización encolable responde 404 tool_not_found.
Si se supera el límite de pendientes de la organización, responde 429 too_many_pending.
Si todos los períodos pedidos ya están activos, responde 409 connection_sync_in_progress.
Si solo algunos lo están, puede encolar los restantes y devolver 202: revisa cada trabajo por su
periodo, sin asumir que el tamaño de job_ids coincide con el de periodos.
Cada mes de una conexión se puede pedir una vez cada 10 minutos, contando también lo que se pidió
desde el panel o al conectar. Si alguno de los períodos que faltan se pidió en ese lapso, responde
429 rate_limited con Retry-After en segundos y no encola ninguno; lo ya guardado se puede consultar.
No hay un parámetro de prioridad para clientes. Si una sincronización es urgente o lleva demasiado
tiempo sin avanzar, escríbenos desde el chat con el conn_..., el período y el sjb_... si lo tienes;
lo revisamos. Esto no garantiza adelanto ni un plazo de término.
Sin un plan activo o con un pago vencido responde 402 (subscription_required o billing_past_due) y no encola nada. Reintentar no cambia el resultado: la sincronización se reanuda al regularizar la cuenta en /billing.
Pedir agosto y septiembre y ver su estado
Ejemplo para una conexión SII con rcv habilitado, con la API key que puede usarla y el permiso
sii:read. En prueba solo se pueden pedir los tres meses más recientes, incluido el corriente
de Chile: agosto y septiembre de 2026 solo caben juntos en esa ventana durante septiembre u
octubre de 2026; para meses anteriores necesitas un plan activo. Requiere curl y jq. Reemplaza
los dos valores antes de copiar el bloque:
(
set -euo pipefail
export EMISSO_CONNECT_API_KEY='connect_sk_REEMPLAZA'
export EMISSO_CONNECT_CONNECTION='conn_REEMPLAZA'
# Lista ids y sistemas; comprueba que elegiste la conexión SII correcta.
curl --fail-with-body -sS https://connect.emisso.ai/api/v1/connections \
-H "Authorization: Bearer $EMISSO_CONNECT_API_KEY" | jq .
# Es periodos (plural), sin sobre input. Conserva la respuesta.
curl --fail-with-body -sS -X POST \
"https://connect.emisso.ai/api/v1/connections/$EMISSO_CONNECT_CONNECTION/syncs" \
-H "Authorization: Bearer $EMISSO_CONNECT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"periodos":["2026-08","2026-09"],"alcances":["rcv"]}' \
-o solicitud-sync.json
# Consulta cada id devuelto; repite este seguimiento hasta que termine.
# No repitas el POST para sondear ni construyas un id a mano.
jq -r '.data.job_ids[]' solicitud-sync.json | while IFS= read -r job_id; do
curl --fail-with-body -sS "https://connect.emisso.ai/api/v1/syncs/$job_id" \
-H "Authorization: Bearer $EMISSO_CONNECT_API_KEY" | jq .data
done
)Ejemplo recortado de una consulta de estado; la siguiente corresponde al otro id y período:
{
"job_id": "sjb_...",
"periodo": "2026-08",
"status": "queued",
"outcomes": null
}Un 202 confirma el encolado. queued o running aún no son resultados; sigue los mismos ids.
Un resultado final exige revisar outcomes, no solo el estado general. Si recibes 409, sigue el
trabajo que ya tenías o revisa la conexión en el panel. Conserva los ids también después de cerrar
la terminal. Para consultar los documentos de esos meses usa sii.rcv.consultar con periodo y el
header X-Connect-Connection; el respaldo XML sii.documentos.* exige acceso de representante y
no está disponible con la clave tributaria de la propia empresa.
GET /v1/syncs/{id}
El estado del trabajo. status es uno de queued, running, succeeded, failed o partial.
curl https://connect.emisso.ai/api/v1/syncs/sjb_... \
-H "Authorization: Bearer connect_sk_..."{
"data": {
"job_id": "sjb_...",
"status": "succeeded",
"targets": ["rcv"],
"outcomes": [{ "alcance": "rcv", "status": "ok", "recordsSynced": 12 }],
"alcance": "rcv",
"periodo": "2026-08",
"trigger": "manual",
"attempts": 1,
"records_synced": 12,
"last_error": null,
"created_at": "2026-08-06T17:11:30.000Z",
"updated_at": "2026-08-06T17:11:44.526Z"
}
}outcomes desglosa el resultado por alcance (ok, partial o failed). Puede ser null cuando no
hay resultados registrados; también puede conservar progreso de un trabajo aún en curso. Mientras
status sea queued o running, sigue consultando el mismo id aunque ya tenga contadores.
succeeded no garantiza que todos los alcances estén completos: revisa los que solicitaste en
outcomes, incluidos sus campos de completitud cuando los declaren. Con failed, consulta
last_error. La guía de seguimiento
explica cada caso y ofrece código con espera acotada. Si prefieres recibir avisos, los eventos
sync.* llegan por webhook firmado; un sync.succeeded también puede contener alcances parciales.
En un trabajo fallido, outcomes puede conservar una entrada con el error y su motivo, o, en
Previred, resultados que un lote anterior alcanzó a confirmar. Un alcance partial también trae su
motivo, igual que cada entrada de perspectivasFallidas. El motivo sale de una lista cerrada
que dice qué pasó y quién tiene que actuar; está en el catálogo de errores.
Webhooks
Todas las consultas requieren webhooks:read. Crear, modificar, deshabilitar, rotar secretos y reenviar
requieren webhooks:write. Los permisos son independientes y se otorgan al crear una API key o consentir
OAuth. Una credencial sin el permiso requerido recibe 403 scope_not_granted antes de resolver el recurso.
La firma de las entregas, los cuerpos de cada evento y las políticas de reintento están en webhooks. El índice de la superficie:
| Método y ruta | Qué hace |
|---|---|
POST /v1/webhooks | Crea un endpoint. La única respuesta que muestra el secret. |
GET /v1/webhooks | Lista tus endpoints. |
GET /v1/webhooks/{id} | Un endpoint puntual. |
PATCH /v1/webhooks/{id} | Modifica url, event_types o enabled. |
DELETE /v1/webhooks/{id} | Deshabilita el endpoint. Es un apagado, no un borrado: reaparece con enabled: false. |
POST /v1/webhooks/{id}/roll-secret | Rota el secret y devuelve el nuevo, una única vez. |
GET /v1/webhooks/{id}/deliveries | Las entregas del endpoint. ?limit= entre 1 y 100, default 50. |
POST /v1/webhooks/{id}/deliveries/{deliveryId}/resend | Reenvía una entrega puntual. |
Los eventos suscribibles son sync.succeeded, sync.failed, sync.partial, connect_session.consumed, sii.carpeta_tributaria.ready y sii.carpeta_tributaria.failed, además de los de boletas de honorarios y de su anulación que lista webhooks. Un event_types vacío suscribe a todos.
POST /v1/webhooks
| Campo | Tipo | Notas |
|---|---|---|
url | string | https y resoluble a una dirección pública; si no, 422 invalid_webhook_url. |
event_types | string[] | Opcional. Vacío = todos los eventos. |
connection_id | string | Opcional. Acota el endpoint a los eventos de una conexión; debe ser de tu organización. |
curl -X POST https://connect.emisso.ai/api/v1/webhooks \
-H "Authorization: Bearer connect_sk_..." \
-H "Content-Type: application/json" \
-d '{"url":"https://app.tu-producto.cl/hooks/connect","event_types":["sync.succeeded","sync.failed"]}'Respuesta (201):
{
"data": {
"id": "whk_...",
"url": "https://app.tu-producto.cl/hooks/connect",
"event_types": ["sync.succeeded", "sync.failed"],
"connection_id": null,
"enabled": true,
"disabled_at": null,
"last_success_at": null,
"created_at": "2026-08-07T12:00:00.000Z",
"secret": "whsec_..."
},
"meta": { "request_id": "req_..." }
}Guarda el secret al recibirlo: las lecturas posteriores (GET /v1/webhooks, GET /v1/webhooks/{id}) devuelven el endpoint sin él.
GET /v1/webhooks/{id}/deliveries
curl "https://connect.emisso.ai/api/v1/webhooks/whk_.../deliveries?limit=2" \
-H "Authorization: Bearer connect_sk_..."{
"data": [
{
"id": "whd_...",
"event_type": "sync.succeeded",
"status": "delivered",
"attempts": 1,
"response_status": 200,
"error": null,
"created_at": "2026-08-07T12:10:00.000Z",
"updated_at": "2026-08-07T12:10:01.000Z"
},
{
"id": "whd_...",
"event_type": "sync.failed",
"status": "failed",
"attempts": 6,
"response_status": 500,
"error": "HTTP 500",
"created_at": "2026-08-06T09:00:00.000Z",
"updated_at": "2026-08-06T18:02:11.000Z"
}
],
"pagination": { "cursor": null, "hasMore": false }
}status es uno de pending, delivering, delivered o failed.
POST /v1/webhooks/{id}/deliveries/{deliveryId}/resend
Reencola una entrega concreta, por ejemplo después de arreglar el receptor.
curl -X POST https://connect.emisso.ai/api/v1/webhooks/whk_.../deliveries/whd_.../resend \
-H "Authorization: Bearer connect_sk_..."{ "data": { "resent": true } }Ejecutar una tool
Es una sola forma: POST /v1/tools/{id}/execute con el cuerpo { "input": { … } }, el header X-Connect-Connection: conn_… cuando el conector tiene conexión, y la respuesta { "data": …, "meta": { "request_id", "tool_id", "plane", "latency_ms", "audit_status" } }. La ficha de cada tool, con su ejemplo, está en la referencia.
Errores frecuentes por superficie
Todos salen del catálogo y traen request_id más suggested_fix.
| Superficie | Códigos que vas a ver |
|---|---|
| Toda la API | 401 unauthorized (falta o falla el bearer, con challenge WWW-Authenticate) · 503 service_unavailable (falla transitoria nuestra, reintenta) · 413 payload_too_large · 400 malformed_request |
| Tools | 404 tool_not_found (el id no existe, o el conector no está habilitado para tu organización) · 400 validation_error · 403 scope_not_granted · 428 connection_credential_required |
| Sincronizaciones | 403 feature_not_in_plan (en prueba de SII o Previred, meses anteriores a los tres más recientes) · 400 validation_error (mes futuro, guías fuera de los seis meses, Santander fuera de sus últimos 90 días o Previred del 10 al 13 para períodos fuera de los tres meses del alta) · 404 tool_not_found (el sistema no ofrece sincronización encolable) · 403 scope_not_granted · 404 connection_not_found (id inexistente o fuera del acceso de tu clave) · 403 connection_disabled (conexión inactiva) · 403 alcance_not_enabled · 409 connection_sync_in_progress · 429 rate_limited (el mismo mes de la conexión antes de 10 minutos, con Retry-After) · 429 too_many_pending · 404 sync_job_not_found |
| Sesiones de conexión | 403 scope_not_granted · 400 validation_error (incluye connection_id ajeno o inconsistente con mode) · 429 rate_limited con Retry-After |
| Webhooks | 403 scope_not_granted · 404 webhook_endpoint_not_found · 404 webhook_delivery_not_found · 422 invalid_webhook_url · 403 feature_not_in_plan |
Los 404 de esta tabla nunca distinguen «no existe» de «existe en otra organización»: esa ambigüedad es deliberada.