# 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](/docs/referencia) y en el [OpenAPI](/docs/api). 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.

<Callout type="info" title="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](/docs/como-consultar/carpeta-tributaria). Los demás endpoints de control se documentan en esta página.
</Callout>

## Convenciones [#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 responde `400 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](/docs/operar/errores).
* Los `GET` de esta página no escriben en la bitácora: no ejecutan ninguna tool. Solo `execute` audita.

## Rutas para conexiones y sincronizaciones [#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 [#descubrimiento]

### `GET /v1/health` [#get-v1health]

Sin credencial. Dice si el gateway está arriba y cuántas tools tiene el registro. El conteo varía con el catálogo.

```bash
curl https://connect.emisso.ai/api/v1/health
```

Salida esperada (el conteo es variable):

```text
data.status = "ok"
data.registry.tools = número actual de tools del registro
```

### `GET /v1` [#get-v1]

Sin credencial. El puntero raíz del API; a diferencia del resto, responde sin el envelope `data`.

```bash
curl https://connect.emisso.ai/api/v1
```

```json
{
  "data_url": "/api/v1",
  "openapi": "/api/v1/openapi.json",
  "docs": "https://connect.emisso.ai/docs"
}
```

### `GET /v1/tools` [#get-v1tools]

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:

```bash
curl https://connect.emisso.ai/api/v1/tools \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{
  "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](/docs/conceptos/sincronizar-consultar).

### `GET /v1/tools/{id}` [#get-v1toolsid]

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`.

```bash
curl https://connect.emisso.ai/api/v1/tools/sii.rcv.consultar \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{ "data": { "id": "sii.rcv.consultar", "…": "la misma ficha de GET /v1/tools" } }
```

### `GET /v1/connectors` [#get-v1connectors]

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`](/docs/referencia/conexiones/sistemas-listar), que además dice qué ya
está conectado.

```bash
curl https://connect.emisso.ai/api/v1/connectors \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{ "data": [{ "code": "core", "name": "Core", "plan": "free", "enabled": true }] }
```

<Callout type="info" title="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`.
</Callout>

### `GET /v1/openapi.json` [#get-v1openapijson]

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`](https://connect.emisso.ai/docs/openapi.json).

```bash
curl -s https://connect.emisso.ai/api/v1/openapi.json | jq .info
```

```json
{
  "title": "Emisso Connect API",
  "version": "0.1.0"
}
```

## Conexiones [#conexiones]

### `GET /v1/connections` [#get-v1connections]

Tus conexiones, cada una la unidad de facturación. De aquí salen los `conn_…` que necesitas para el header `X-Connect-Connection`.

```bash
curl https://connect.emisso.ai/api/v1/connections \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{
  "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.

<Callout type="info" title="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.
</Callout>

## Sesiones de conexión [#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](/docs/plataformas/enlace-hosted). Aquí, el contrato de los dos endpoints.

### `POST /v1/connect_sessions` [#post-v1connect_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](/docs/plataformas/enlace-hosted#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](/docs/plataformas/enlace-hosted#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.

```bash
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`):

```json
{
  "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_..." }
}
```

<Callout type="warn" title="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.
</Callout>

La [carpeta tributaria puntual](/docs/como-consultar/carpeta-tributaria) 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` [#get-v1connect_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.

```bash
curl "https://connect.emisso.ai/api/v1/connect_sessions?connection_id=conn_..." \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{
  "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 [#carpeta-tributaria-puntual]

Requiere `sii:carpeta_tributaria:generar`. La [guía de carpeta tributaria](/docs/como-consultar/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 [#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` [#post-v1connectionsconnsyncs]

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.            |

```bash
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:

```json
{ "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](https://connect.emisso.ai/billing).

### Pedir agosto y septiembre y ver su estado [#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:

```bash
(
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:

```json
{
  "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}` [#get-v1syncsid]

El estado del trabajo. `status` es uno de `queued`, `running`, `succeeded`, `failed` o `partial`.

```bash
curl https://connect.emisso.ai/api/v1/syncs/sjb_... \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{
  "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](/docs/conceptos/sincronizar-consultar#cómo-sabes-que-terminó)
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](/docs/referencia/errores#motivos-de-un-fallo).

## Webhooks [#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](/docs/operar/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](/docs/operar/webhooks). Un `event_types` vacío suscribe a todos.

### `POST /v1/webhooks` [#post-v1webhooks]

| 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. |

```bash
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`):

```json
{
  "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` [#get-v1webhooksiddeliveries]

```bash
curl "https://connect.emisso.ai/api/v1/webhooks/whk_.../deliveries?limit=2" \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{
  "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` [#post-v1webhooksiddeliveriesdeliveryidresend]

Reencola una entrega concreta, por ejemplo después de arreglar el receptor.

```bash
curl -X POST https://connect.emisso.ai/api/v1/webhooks/whk_.../deliveries/whd_.../resend \
  -H "Authorization: Bearer connect_sk_..."
```

```json
{ "data": { "resent": true } }
```

## Ejecutar una tool [#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](/docs/referencia).

## Errores frecuentes por superficie [#errores-frecuentes-por-superficie]

Todos salen del [catálogo](/docs/operar/errores) 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.


---

Esta página es parte de la documentación de Emisso Connect. El índice completo, con cada página y su descripción, está en https://connect.emisso.ai/llms.txt. Esta misma página en HTML: https://connect.emisso.ai/docs/operar/api-control.
