# Enlace hosted de conexión

> Un enlace de un solo uso para que un tercero entregue la clave del SII o del banco: sin cuenta, sin acceso al dashboard y sin que la credencial pase por tu aplicación.



Quien tiene las credenciales de una fuente casi nunca es quien administra Emisso: la clave del SII la tiene el contador, la del banco la tiene el dueño. El **enlace hosted** resuelve eso con una **sesión de conexión**: un enlace de un solo uso que permite entregar credenciales de **una fuente concreta**, para **una organización concreta**, sin crear una cuenta y sin dar acceso a nada más del dashboard.

El ciclo son cinco pasos: creas la sesión, envías el enlace, la persona entrega sus credenciales, tu aplicación se entera por `postMessage` (si lo incrustaste) o por `redirect_uri`, y además recibes un [webhook](/docs/operar/webhooks). Todo queda en la bitácora de la conexión.

## La vía sin código [#la-vía-sin-código]

Cuando no estás incrustando el widget en un producto, no necesitas la API: un agente conectado por [MCP](/docs/agentes/mcp) acuña el enlace en medio de la conversación con `conexiones.enlace.crear`. La tool viaja dentro de la meta-tool `execute_write`, porque escribe:

```json
{ "tool": "conexiones.enlace.crear", "params": { "sistema": "sii" } }
```

Desde tu código, la misma tool se llama por REST, en [`POST /api/v1/tools/conexiones.enlace.crear/execute`](/docs/api/conexiones__enlace__crear). Salida esperada, por cualquiera de los dos caminos (por REST llega dentro de `data`):

```json
{
  "url": "https://connect.emisso.ai/c/mYw2kQ81xR4tPnZs",
  "dominio": "connect.emisso.ai",
  "sistema": "sii",
  "sistemaNombre": "Servicio de Impuestos Internos",
  "modo": "crear",
  "expiraEn": "2026-08-07T15:32:11.000Z",
  "intentosMaximos": 5,
  "acceso": null,
  "emitir": null,
  "alcances": null,
  "advertencia": "Este enlace pide credenciales de acceso al sistema. Muéstralo siempre con su dominio completo y di quién lo pidió y para qué. No lo presentes como un aviso del banco ni del SII."
}
```

Con `acceso`, `emitir` y `alcances` el agente fija lo mismo que `credential_scheme`, `emitir` y `alcances` en la API (ver la tabla de abajo); los códigos de acceso salen de `accesos` en `conexiones.sistemas.listar`. Si los omite, los elige la persona.

Este carril está deliberadamente recortado: el enlace vence en 1 hora (la persona está presente ahora), no acepta `allowed_origins` ni `redirect_uri` (los dos campos que lo volverían un vector de clickjacking y un open redirect en manos de un modelo de lenguaje) y tiene su propio tope de emisión por actor. La `advertencia` viaja ya escrita para que el agente la muestre tal cual.

Todo lo que sigue en esta página es la vía de integrador: `POST /v1/connect_sessions` con API key, que sí admite incrustar el widget y controlar el retorno.

## 1. Crear la sesión [#1-crear-la-sesión]

```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",
    "credential_scheme": "portal_credentials_representante",
    "emitir": true,
    "alcances": ["rcv", "documentos"],
    "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-07-29T12:00:00.000Z",
    "consumed_at": null,
    "resulting_connection_id": null,
    "created_at": "2026-07-28T12:00:00.000Z",
    "credential_scheme": "portal_credentials_representante",
    "emitir": true,
    "alcances": ["rcv", "documentos"],
    "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">
  La `url` con el token en claro se devuelve **únicamente** en esta respuesta. La base guarda solo su `sha256` y un prefijo visible para nombrarlo en una lista, igual que una API key (`connect_sk_`). Si lo pierdes, emite otra sesión y revoca la anterior: no hay forma de recuperarlo.
</Callout>

| Parámetro           | Qué hace                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `mode`              | `create` (por defecto) emite una conexión nueva; `reauth` reemplaza la credencial de una conexión existente y exige `connection_id`; `activar_emision` hace que una conexión del SII que ya lee pase a emitir y exige `connection_id` (ver [Activar la emisión en una conexión existente](#activar-la-emisión-en-una-conexión-existente)); `cambiar_clave_certificado` le pide a la empresa la clave nueva del certificado de una conexión que ya emite y exige `connection_id` (ver [Cambiar la clave del certificado](#cambiar-la-clave-del-certificado)). |
| `connector_code`    | El código de una fuente conectable, por ejemplo `sii`, `bch_empresas` o `itau_empresas`. El enlace queda fijado a esa fuente: quien lo abre no elige otra. Los códigos válidos son los `codigo` que devuelve `conexiones.sistemas.listar`.                                                                                                                                                                                                                                                                                                                   |
| `credential_scheme` | Opcional. El acceso con el que entra la persona, por ejemplo `portal_credentials` (clave de la empresa) o `portal_credentials_representante` (clave de una persona que la representa) en el SII. Queda fijo en la conexión. Omitido, lo elige quien abre el enlace. Los códigos son los `codigo` de `accesos` en `conexiones.sistemas.listar`.                                                                                                                                                                                                               |
| `emitir`            | Opcional. `true` crea una conexión que emite documentos tributarios: exige un acceso que pueda emitir (si el sistema tiene uno solo, se fija ese) y que la emisión esté habilitada para tu organización. Quien abre el enlace tendrá que entregar la clave del certificado digital. `false` crea una conexión que solo lee. Omitido, lo decide la persona.                                                                                                                                                                                                   |
| `alcances`          | Opcional. Los módulos de datos que se habilitan, servidos por el acceso fijado (o por todos los accesos, si no fijas uno). Omitido, los elige la persona.                                                                                                                                                                                                                                                                                                                                                                                                    |
| `display_name`      | Opcional, solo en `create`. El nombre de la conexión que nazca del enlace, de 1 a 80 caracteres: el que ves en el panel, la bitácora y los cobros. Quien abre el enlace no lo ve. Omitido, la conexión se llama como su sistema (`SII`, `SII 2`). Si otra conexión de ese sistema ya lo usa, responde `400 validation_error`. Para cambiarlo después, usa `conexiones.conexion.renombrar`.                                                                                                                                                                   |
| `connection_id`     | Obligatorio en `reauth`, `activar_emision` y `cambiar_clave_certificado`. Una conexión de otra organización es imposible de referenciar: lo impide una llave foránea compuesta en el esquema, no una validación de código.                                                                                                                                                                                                                                                                                                                                   |
| `allowed_origins`   | Hasta 5 orígenes `https` exactos, sin comodines ni path. Es la allowlist del iframe y el destino del `postMessage`.                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `redirect_uri`      | A dónde volver al terminar. Debe ser `https`, sin fragmento y, si registraste orígenes, pertenecer a ese conjunto.                                                                                                                                                                                                                                                                                                                                                                                                                                           |

`credential_scheme`, `emitir` y `alcances` exigen `connector_code` y `mode: "create"`; al volver a vincular, la conexión ya tiene su acceso. Una combinación imposible (un acceso que el sistema no tiene, emitir con un acceso que no emite, un módulo que el acceso no sirve) devuelve `400 validation_error` sin crear nada. Todos son opcionales: una integración que no los manda sigue funcionando igual.

Un campo desconocido en el cuerpo devuelve `400 validation_error`: el esquema es estricto a propósito, porque un campo mal escrito es alguien creyendo que está configurando algo. La emisión está limitada por tasa (60 enlaces por hora y por organización); al excederla la respuesta es `429` con `Retry-After`. Los topes completos están en [límites](/docs/operar/limites).

Emitir un enlace abre una conexión facturable, así que sin un plan activo la respuesta es `402 subscription_required` y con un pago vencido, `402 billing_past_due`. Si la organización queda sin plan después de emitirlo, quien abre el enlace ve que no está disponible y no se le piden credenciales.

**Vencimiento:** 24 horas en `mode: "create"`, `mode: "activar_emision"` y `mode: "cambiar_clave_certificado"` (margen para que el contador lo abra al día siguiente) y 1 hora en `mode: "reauth"` (se emite frente a una credencial ya rota, con la persona presente). **Intentos:** 5 por sesión; un formulario mal tipeado no gasta ninguno, solo cuenta un intento real contra la fuente. **Consumo:** solo al éxito, así que una clave equivocada no quema el enlace: se reintenta sin pedir otro.

`GET /v1/connect_sessions` lista las sesiones con su `token_prefix`, su estado y sus intentos; nunca el token. Acepta `?connection_id=conn_…` para acotar la lista a una [conexión](/docs/conceptos/conexiones).

También puedes emitir el enlace desde el dashboard, sin escribir código: en **Conexiones** para el modo `create` (ahí siempre eliges el acceso, la emisión y los datos antes de crearlo), y en los ajustes de una conexión para los modos `reauth`, `activar_emision` (en **Emisión en el SII**, «Pedir la clave a la empresa») y `cambiar_clave_certificado` (en **Clave del certificado digital**, «Pedir la clave a la empresa»). La guía [Pedir la clave con un enlace](/docs/como-conectar/enlace) recorre ese camino con una demo animada del panel y de la pantalla que recibe la persona; sirve para mandársela a quien no integra la API. Ahí mismo se **revoca**: la sesión pasa a `status: "revoked"`, igual que se revoca una API key, porque no hay borrado en ningún punto de este flujo. El enlace emitido desde el dashboard nace sin `allowed_origins`, así que no es incrustable: para el iframe usa la API.

## 2. Abrir el enlace [#2-abrir-el-enlace]

La persona abre `https://connect.emisso.ai/c/<token>`, ve el nombre de tu organización y la fuente, entrega sus credenciales y recibe la confirmación. No hay registro, ni contraseña, ni acceso a ninguna otra pantalla.

La versión web de esta página trae una **demo interactiva** de esa pantalla: el mismo componente que corre detrás de un enlace de verdad, con una acción falsa que no crea ninguna conexión, no llama al SII y nunca lee la clave que escribas. Puedes recorrerla entera con datos inventados. Si estás leyendo el markdown, el recorrido equivalente está descrito paso a paso en [Qué verá la persona](#qué-verá-la-persona).

<WidgetDemo />

Si el token no existe, venció, ya fue consumido o agotó sus intentos, la página responde el **mismo 404**, sin cuerpo diferenciado. Así, quien pruebe tokens no puede distinguir cuáles existieron o fueron usados.

### Qué verá la persona [#qué-verá-la-persona]

El recorrido completo, para que puedas anticiparlo cuando entregues el enlace, y para que un agente que lo acuña con `conexiones.enlace.crear` sepa qué explicar:

1. **La cabecera dice para quién es.** «Conexión para *\<tu organización>*», y si el enlace lo pidió otra empresa, además «Solicitado por». Nunca se presenta como un aviso del banco ni del SII.
2. **Si no fijaste el acceso y la fuente tiene más de uno, lo elige primero.** El SII ofrece «Con clave de empresa» y «Con clave de persona». Marcar un radio no avanza: hay que confirmar con «Continuar». Con `credential_scheme` fijado, este paso no aparece. La fuente ya viene fijada por el enlace; eso no se elige.
3. **Escribe sus credenciales.** Los campos salen del acceso (para el SII con clave de empresa, RUT de la empresa y clave) y el RUT se formatea solo. Si fijaste `emitir: true`, el bloque de emisión aparece activo y pide la clave del certificado digital, que es obligatoria. La clave nunca entra a estado de React ni a la analítica.
4. **Ve qué datos se van a conectar.** Con `alcances` fijado, los ve como lista y no los cambia. Si no lo fijaste, «Datos que conectarás» abre una hoja donde puede elegirlos.
5. **Espera mientras Emisso comprueba la clave.** El indicador de progreso refleja hitos persistidos del intento (credencial guardada, acceso confirmado, módulos consultados, registros guardados). Un banco puede tardar cerca de un minuto. Puede cerrar la pestaña: el trabajo sigue en el servidor.
6. **Ve el cierre.** Si el acceso quedó verificado, la confirmación y el aviso de que puede cerrar. Si la fuente rechazó la clave, el error aparece **junto al campo** y el enlace **sigue vivo**: reintenta ahí mismo, hasta agotar los 5 intentos, sin pedir otro enlace.

Lo que **no** ve en ningún momento: el dashboard, otras conexiones de tu organización, otros sistemas, ni una pantalla de registro.

### Sin precios de Emisso [#sin-precios-de-emisso]

Quien abre el enlace nunca ve precios de Emisso, tampoco al marcar emitir. El costo de la conexión lo ve
quien la crea: en el panel, antes de crear el enlace; por un agente, en `costoAdicional`.

### Con la marca de tu organización [#con-la-marca-de-tu-organización]

La página puede mostrar tu nombre, tu logo y tu color en vez de la marca de Emisso. Se pide a soporte, que la
activa por organización; desde ahí, quien administra la organización la configura en **Organización →
Ajustes → Marca de tus enlaces** y se aplica a todos sus enlaces, también a los que crea la API y a la página
incrustada. No hay campos nuevos en `POST /v1/connect_sessions`.

* **Nombre visible:** entre 2 y 60 caracteres. No puede incluir el nombre de un banco, del SII u otro sistema
  conectable, ni «Emisso».
* **Logo:** exactamente **256 × 256 px**, PNG o WebP, hasta 256 KB. Lo importante tiene que caber en el círculo
  central de 200 px, porque el cuadro tiene las esquinas redondeadas. Se muestra a 34 px en la cabecera y a 44 px
  en el cierre.
* **Color de acento:** un hexadecimal `#rrggbb` para el botón, la barra de avance y el fondo del logo. El color
  del texto encima se elige solo para que contraste.

Con o sin marca, la página sigue mostrando el dominio `connect.emisso.ai`, «Protegido por Emisso Connect» y el
logo oficial del sistema que se conecta. Así nadie puede hacer pasar el enlace por un aviso del banco o del SII.

## 3. Incrustarlo en tu producto [#3-incrustarlo-en-tu-producto]

Con `allowed_origins` poblado, la página se puede incrustar. La cabecera `frame-ancestors` se construye por sesión desde esa lista, con `'none'` como valor por defecto cuando está vacía.

```html
<iframe
  src="https://connect.emisso.ai/c/connect_cs_..."
  title="Conectar el SII"
  style="width:100%;height:640px;border:0"
></iframe>
```

Reglas: orígenes `https` exactos (`https://app.tu-producto.cl`, sin path ni comodín), máximo 5, y si no registras ninguno el iframe no carga.

## 4. Escuchar el resultado en el navegador [#4-escuchar-el-resultado-en-el-navegador]

Cuando el flujo llega a su estado final, la página emite un `postMessage` **hacia cada origen exacto** que registraste, nunca `"*"`. El mensaje no lleva la credencial ni el token:

```ts
window.addEventListener("message", (event) => {
  if (event.origin !== "https://connect.emisso.ai") return;   // verifica SIEMPRE el origen
  const msg = event.data as { type: string; status: string; connection_id: string | null };
  if (msg.type !== "emisso:connect") return;
  if (msg.status === "connected") {
    console.log("entrega aceptada:", msg.connection_id);
  } else {
    console.warn("la entrega no fue aceptada; el enlace sigue vivo hasta que venza");
  }
});
```

El mensaje es exactamente `{ type: "emisso:connect", status: "connected" | "failed", connection_id: string | null }` y nada más, y llega **una sola vez**: hay un único emisor.

Los dos estados son terminales, pero significan cosas distintas de lo que sugiere el nombre:

* `connected`: Emisso aceptó la entrega, vinculó la credencial y consumió el enlace. `connection_id` identifica la conexión creada o actualizada. No implica necesariamente que el login o la primera sincronización ya hayan terminado: cuando la comprobación no puede hacerse en línea, Emisso deja un trabajo durable en cola antes de emitir este estado.
* `failed`: el flujo terminó sin una entrega que el anfitrión pueda tratar como completada. `connection_id` es `null` siempre, aunque el intento haya alcanzado a crear una conexión o guardar una credencial. El mismo enlace retoma esa conexión sin duplicarla; si no quedó una comprobación durable en cola, la persona tendrá que ingresar la clave otra vez porque los campos secretos no sobreviven al cierre de la pestaña.

Una clave rechazada por la fuente **no** es un estado terminal y no emite nada: el enlace sigue vivo y la persona reintenta en la misma pantalla, hasta agotar los 5 intentos.

El reintento **rota la credencial sobre la conexión que el enlace ya creó**, nunca crea una segunda. Eso vale también si la persona recarga la página o vuelve a abrir el enlace más tarde: el vínculo vive en la sesión, no en la pestaña. Por eso una sesión todavía `pending` puede traer `resulting_connection_id` con un `conn_…` ya poblado: significa «este enlace ya creó esta conexión y está rotando sobre ella», no que se haya consumido. Lo que marca el consumo sigue siendo `status: "consumed"` con su `consumed_at`.

Si registraste un `redirect_uri`, al llegar a ese estado final la página navega una sola vez (con `history.replace`, para que el botón «atrás» no lleve a un enlace ya consumido) agregando `status=connected|failed` a tu URL, y `connection_id` solo cuando la entrega fue aceptada. El token nunca viaja en esa redirección; si tu `redirect_uri` traía un `token` propio, se elimina antes de navegar.

Si no registraste orígenes y no hay `redirect_uri`, no se emite ningún mensaje ni se navega: el resultado se consulta con `GET /v1/connections` o llega por webhook.

## 5. Recibir el webhook [#5-recibir-el-webhook]

Cuando el enlace se usa con éxito, Emisso emite el evento **`connect_session.consumed`** por el mismo bus firmado que los eventos `sync.*` (`POST /v1/webhooks` para registrar un endpoint; la entrega trae `webhook-id`, `webhook-timestamp` y `webhook-signature`, y [la firma se verifica igual que siempre](/docs/operar/webhooks)).

La [carpeta tributaria puntual](/docs/como-consultar/carpeta-tributaria) tiene su propio flujo: aceptar el enlace no
emite `connect_session.consumed`. Su resultado llega con `sii.carpeta_tributaria.ready` o
`sii.carpeta_tributaria.failed`, después de cerrar el acceso temporal.

```json
{
  "type": "connect_session.consumed",
  "timestamp": "2026-07-28T12:00:00.000Z",
  "data": {
    "session": { "id": "cs_7f3ab9c1d2e4f5061728" },
    "connection": { "id": "conn_...", "connector": "sii" },
    "verified": true,
    "consumed_at": "2026-07-28T12:00:00.000Z"
  }
}
```

`verified` distingue tres casos: `true` (la verificación contra la fuente corrió y pasó), `false` (corrió y falló; la conexión existe igual, con la credencial que se entregó) y `null` (no hubo un veredicto en línea). `null` puede significar que el conector no declara verificación inmediata o que la comprobación quedó diferida en un trabajo durable; no significa que la fuente ya haya aceptado la credencial ni que los datos estén disponibles. Para disponibilidad de datos, espera el webhook `sync.succeeded` o consulta el estado de la conexión.

<Callout type="info" title="La conexión no espera al webhook">
  Si tu endpoint está caído, la conexión **igual quedó creada**: la entrega se reintenta con backoff y el resultado real está siempre en `GET /v1/connections`. Nunca hagas depender el alta de haber recibido el evento.
</Callout>

## 6. Auditoría [#6-auditoría]

Todo lo que hizo el enlace queda en la pestaña **Bitácora** de la conexión, atribuido con honestidad: las filas de configuración muestran `enlace de conexión (cs_…)` como actor (ni la persona que emitió el enlace, que no entregó nada, ni «sistema», que sería falso), y la verificación contra la fuente aparece en las ejecuciones porque pasa por el mismo `gateway.execute()` que cualquier otra llamada. Qué guarda cada fila y cómo leerla está en [la bitácora](/docs/conceptos/bitacora).

## Activar la emisión en una conexión existente [#activar-la-emisión-en-una-conexión-existente]

Si tu cliente conectó el SII solo para leer y ahora quiere emitir, no hace falta borrar la conexión ni crear otra. Un enlace con `mode: "activar_emision"` le pide **solo la clave del certificado digital**: la misma conexión, con el mismo `conn_…`, sus datos y su sincronización, pasa a emitir facturas, notas de crédito y boletas. Tu cliente no necesita cuenta en Emisso ni entrar al panel.

Sirve solo para conexiones que entraron **con clave de persona** (`credential_scheme: "portal_credentials_representante"` en `GET /v1/connections`). La clave tributaria de la empresa no emite y el acceso de una conexión no cambia: para esas conexiones crea una conexión nueva con `mode: "create"`, `credential_scheme: "portal_credentials_representante"` y `emitir: true`.

```bash
curl -X POST https://connect.emisso.ai/api/v1/connect_sessions \
  -H "Authorization: Bearer connect_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "activar_emision",
    "connection_id": "conn_4ly7hnmw2tigryuxfjkr4",
    "redirect_uri": "https://app.tu-producto.cl/conexiones/listo"
  }'
```

```json
{
  "data": {
    "id": "cs_4b8ad2c7e1f3a5069172",
    "mode": "activar_emision",
    "connector_code": "sii",
    "connection_id": "conn_4ly7hnmw2tigryuxfjkr4",
    "token_prefix": "connect_cs_QrS…",
    "allowed_origins": [],
    "redirect_uri": "https://app.tu-producto.cl/conexiones/listo",
    "status": "pending",
    "attempts": 0,
    "max_attempts": 5,
    "expires_at": "2026-11-08T12:00:00.000Z",
    "consumed_at": null,
    "resulting_connection_id": null,
    "created_at": "2026-11-07T12:00:00.000Z",
    "credential_scheme": null,
    "emitir": null,
    "alcances": null,
    "display_name": null,
    "url": "https://connect.emisso.ai/c/connect_cs_..."
  },
  "meta": { "request_id": "req_..." }
}
```

La respuesta es la misma de `create`, con `mode: "activar_emision"` y la `url` del enlace. `connector_code` es opcional (sale de la conexión); `credential_scheme`, `emitir`, `alcances` y `display_name` no aplican y devuelven `400 validation_error`. `allowed_origins` y `redirect_uri` funcionan igual que en el alta, así que el enlace también se puede incrustar.

| Respuesta                                        | Cuándo                                                                                                                                             |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `201`                                            | El enlace quedó creado. Si había otro enlace de activación vivo para esa conexión, queda revocado.                                                 |
| `400 validation_error`                           | La conexión no es de tu organización o no es del SII, el cuerpo trae un campo que no aplica, o la emisión no está habilitada para tu organización. |
| `409 requiere_clave_de_persona`                  | La conexión entra con la clave tributaria de la empresa. Crea una conexión nueva con clave de persona y `emitir: true`.                            |
| `409 emision_ya_activa`                          | La conexión ya emite. No hay nada que pedir.                                                                                                       |
| `428 connection_credential_required`             | La credencial de la conexión falta o no sirve. Renuévala primero con `mode: "reauth"`.                                                             |
| `403 connection_disabled`                        | La conexión está pausada.                                                                                                                          |
| `402 subscription_required` / `billing_past_due` | El mismo corte por cobranza del alta: emitir hace que la conexión cuente como emisora.                                                             |

La guía [Activar la emisión con un enlace desde tu sistema](/docs/plataformas/activar-emision-enlace) recorre esta llamada y la pantalla que ve tu cliente, con una demo animada.

**Lo que ve tu cliente:** quién lo pide, el RUT de la empresa (que no se edita), que la lectura sigue igual y que se suma emitir facturas, notas de crédito y boletas. Escribe la clave del certificado y presiona «Activar emisión». Emisso comprueba en el SII que la empresa esté inscrita en el Facturador Gratuito y que haya un certificado digital para firmar. Si el SII tiene más de un giro o una dirección para la empresa, elige cuál va en los documentos. No ve precios de Emisso.

**Envía el enlace solo a quien tiene la clave del certificado:** funciona como una autorización y quien lo completa activa la emisión de la empresa. Si la clave no es la correcta, el primer documento no sale y pides la nueva con `mode: "cambiar_clave_certificado"`.

**El enlace se consume solo cuando la emisión queda activa.** Si la empresa no está inscrita en el Facturador o no tiene un certificado cargado en el SII, la pantalla dice qué hacer y el enlace sigue vivo hasta que vence, para que tu cliente lo resuelva en sii.cl y vuelva a intentarlo. Cada intento que llega al SII gasta uno de los 5.

**Para saber que terminó:** el webhook `connect_session.consumed` de ese enlace (su `session.id` es el que recibiste al crearlo) llega cuando la conexión ya emite. Desde ahí emites con `sii.dte.emitir` o `sii.boleta.emitir` y el mismo `connectionId`. Al pasar a emitir, la conexión cuenta como conexión que emite en tu plan.

Un agente pide el mismo enlace con `conexiones.enlace.crear`, `modo: "activar_emision"` y `conexionId`. Desde el panel, en los ajustes de la conexión, **Emisión en el SII → Pedir la clave a la empresa**.

## Cambiar la clave del certificado [#cambiar-la-clave-del-certificado]

Cuando la empresa renueva su certificado digital, la clave suele cambiar, y el SII rechaza la firma de la clave anterior. Si tu cliente no entra al panel, un enlace con `mode: "cambiar_clave_certificado"` le pide **solo la clave nueva del certificado**. La conexión sigue emitiendo con el mismo `conn_…`; no cambia su acceso, su empresa ni su clave tributaria.

Sirve para conexiones del SII que **ya emiten**. Para una conexión que todavía no emite, usa `activar_emision`, que también pide la clave del certificado.

```bash
curl -X POST https://connect.emisso.ai/api/v1/connect_sessions \
  -H "Authorization: Bearer connect_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "cambiar_clave_certificado",
    "connection_id": "conn_4ly7hnmw2tigryuxfjkr4"
  }'
```

```json
{
  "data": {
    "id": "cs_9c2de7a1b3f4e5061829",
    "mode": "cambiar_clave_certificado",
    "connector_code": "sii",
    "connection_id": "conn_4ly7hnmw2tigryuxfjkr4",
    "token_prefix": "connect_cs_XyZ…",
    "allowed_origins": [],
    "redirect_uri": null,
    "status": "pending",
    "attempts": 0,
    "max_attempts": 5,
    "expires_at": "2026-11-10T12:00:00.000Z",
    "consumed_at": null,
    "resulting_connection_id": null,
    "created_at": "2026-11-09T12:00:00.000Z",
    "credential_scheme": null,
    "emitir": null,
    "alcances": null,
    "display_name": null,
    "url": "https://connect.emisso.ai/c/connect_cs_..."
  },
  "meta": { "request_id": "req_..." }
}
```

La forma del cuerpo es la misma de `activar_emision`: `connector_code` es opcional, `credential_scheme`, `emitir`, `alcances` y `display_name` no aplican, y `allowed_origins` y `redirect_uri` funcionan igual que en el alta. Este enlace no suma nada a tu cobro: la conexión ya emitía.

| Respuesta                            | Cuándo                                                                                                                                             |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `201`                                | El enlace quedó creado. Si había otro enlace vivo para cambiar la clave de esa conexión, queda revocado.                                           |
| `400 validation_error`               | La conexión no es de tu organización o no es del SII, el cuerpo trae un campo que no aplica, o la emisión no está habilitada para tu organización. |
| `409 emision_no_activa`              | La conexión todavía no emite. Pide un enlace con `mode: "activar_emision"`.                                                                        |
| `409 requiere_clave_de_persona`      | La conexión entra con la clave tributaria de la empresa, que no firma documentos.                                                                  |
| `428 connection_credential_required` | La credencial de la conexión falta o no sirve. Renuévala primero con `mode: "reauth"`.                                                             |
| `403 connection_disabled`            | La conexión está pausada.                                                                                                                          |

**Lo que ve tu cliente:** quién lo pide, el RUT de la empresa, que la lectura de datos y la emisión siguen igual, y un solo campo para la clave del certificado. Presiona «Guardar clave» y ve «Clave guardada». No ve precios de Emisso.

**La clave se comprueba con el próximo documento que emitas.** Una clave del certificado solo se puede probar firmando, así que Emisso la guarda sin comprobarla y la verifica en la siguiente vista previa. Si la empresa se equivocó, la vista previa responde `sii_signing_material_invalid` y puedes pedir otro enlace. Guardar la clave no gasta intentos: el enlace se consume apenas la empresa la guarda.

**Para saber que terminó:** el webhook `connect_session.consumed` de ese enlace llega con `verified: false`, porque la clave todavía no se usó para firmar.

Un agente pide el mismo enlace con `conexiones.enlace.crear`, `modo: "cambiar_clave_certificado"` y `conexionId`. Desde el panel, en los ajustes de la conexión, **Clave del certificado digital → Pedir la clave a la empresa**.

## Modelo de amenaza, en corto [#modelo-de-amenaza-en-corto]

| Riesgo                          | Qué lo acota                                                                                                                                                               |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Fuerza bruta del token          | 256 bits de entropía, búsqueda por igualdad exacta sobre índice único, tope de intentos, expiración corta, el mismo 404 en los cuatro fallos y un limitador de tasa por IP |
| Reuso del enlace                | Un solo uso ganado por la base de datos, no por un chequeo previo en código                                                                                                |
| Clickjacking                    | `frame-ancestors` por sesión, orígenes exactos, sin comodines, `'none'` por defecto                                                                                        |
| Exfiltración por `postMessage`  | `targetOrigin` siempre exacto; el mensaje nunca lleva la credencial ni el token                                                                                            |
| Redirector abierto              | `redirect_uri` obligatoriamente `https`, sin fragmento, y dentro de `allowed_origins` cuando los registraste                                                               |
| Reenvío del enlace a un tercero | Expiración, un solo uso, tope de intentos y **notificación**: la fila en la bitácora más este webhook                                                                      |

El cuadro completo de qué pasa con la credencial una vez entregada (cifrado, aislamiento, revocación) está en [seguridad](/docs/conceptos/seguridad).


---

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/plataformas/enlace-hosted.
