> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tupana.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tupana Connect: conectar empresas al SII desde tu SaaS

> Tupana Connect es el widget con el que tus usuarios conectan su empresa (SII, facturador de mercado o banco) a tu SaaS sin que veas su clave. Sesión, iframe, eventos y webhook connection.active.

# Tupana Connect

El widget que abres en tu app para que **tu usuario final** conecte su
empresa. La clave del SII (o del facturador, o del banco) viaja a Tupana.
Tu backend nunca la ve.

En v2 la clave no se manda por API: conectas con este widget y guardas el
`connection_id` (`cred_…`), que es lo único que tu SaaS conoce de la empresa.

![Tupana Connect — SII gratuito](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/02-sii-gratuito.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=78db33de13529c881e7deb04b568e05f)

Esto ve tu usuario. Tú solo recibes el cred\_….

## Integra en tres llamadas

**1. Tu backend abre una sesión**

`POST /api/v2/connect-sessions/` con tu API key. Te devuelve un
`client_token` de 30 minutos. Si ya conoces el RUT, mándalo como `tax_id`
y el widget no lo vuelve a pedir.

```bash theme={null}
curl -X POST "https://api.tupana.ai/v2/connect-sessions/" \
  -H "Authorization: Api-Key TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "capabilities": ["invoicing"],
    "app_name": "Mi SaaS",
    "tax_id": "78.152.641-K",
    "external_customer_id": "cus_912"
  }'
```

```json theme={null}
{
  "connect_session_id": "cses_7391028471023",
  "client_token": "cst_9f2b…",
  "expires_at": "2026-09-25T18:30:00Z"
}
```

**2. Tu frontend abre el widget**

Iframe o ventana. El `token` es el `client_token`. `tax_id` en la URL
gana sobre el de la sesión.

```html theme={null}
<iframe src="https://developers.tupana.ai/connect?token=cst_9f2b…&tax_id=78.152.641-K"
        width="460" height="660" frameborder="0"></iframe>
```

```js theme={null}
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://developers.tupana.ai') return;
  if (event.data.source !== 'tupana-connect') return;

  if (event.data.event === 'success') {
    // event.data.connection_id → "cred_…" (es la credencial)
    closeModal();
  }
  if (event.data.event === 'exit') closeModal();
});
```

| `event` | Para qué |
| - | - |
| `open` | El widget cargó |
| `success` | El flujo terminó. Cierra el modal. **No** es la fuente de verdad |
| `exit` | El usuario cerró antes de terminar |

> [!WARNING]
>
> `success` y `exit` solo mueven tu
> interfaz. La conexión existe cuando tu backend recibe
> `connection.active` o
> `GET /api/v2/connections/enrollments/{enrollment_id}/` la muestra lista.

**3. Tu backend guarda la conexión**

Cuando el SII valida la clave, el webhook trae el `cred_…`. Con eso emites.
También puedes listarlas con
[`GET /connections/`](https://docs.tupana.ai/api-reference/v2/connections/list).

```json theme={null}
{
  "event": "connection.active",
  "data": {
    "connection_id": "cred_5028174639201",
    "company_id": "ment_4829301923847",
    "external_customer_id": "cus_912"
  }
}
```

### Body de la sesión

| Campo | Requerido | Qué hace |
| - | - | - |
| `capabilities` | | `invoicing`, `sii_data`, `banking`. Default `["invoicing"]` |
| `tax_id` | | RUT de la empresa. El widget salta ese paso |
| `app_name` | | El nombre que ve el usuario adentro |
| `external_customer_id` | | Tu id de cliente. Vuelve en el webhook |
| `environment` | | `sandbox` o `production`. Default: el de la API key |
| `return_url` | | A dónde vuelve al terminar |

### Query del widget

| Parámetro | Requerido | Qué hace |
| - | - | - |
| `token` | Sí | El `client_token` |
| `tax_id` | | RUT. Si viene acá y en la sesión, gana la URL |
| `rut` | | Alias de `tax_id` |

> [!WARNING]
>
> El `client_token` es de un solo uso y dura 30 minutos. Créalo cuando el
> usuario va a conectar, no antes. Una API key de sandbox no abre producción.

## Reautorizar un banco

Cuando una conexión de banco queda en `requires_action`, no abras una sesión
desde cero: [`POST /connections/{connection_id}/reauthorizations/`](https://docs.tupana.ai/api-reference/v2/connections/reauthorize)
te devuelve un `client_token` con la empresa y el banco ya elegidos, y la
conexión conserva su `connection_id` al terminar.

## Lo que ve tu usuario

**SII gratuito**

![Widget: RUT de la empresa](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/01-empresa.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=14faed4c37a6d5cdc869ce24e0f75f6f)

1. RUT de la empresa — no aparece si mandaste tax\_id.

![Widget: SII gratuito](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/02-sii-gratuito.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=78db33de13529c881e7deb04b568e05f)

2. Detectamos SII. El RUT del representante se formatea solo.

**Facturador de mercado**

![Widget: facturador detectado](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/03-mercado-detectado.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=c8d4a32fa0c2685ade66f011255d50f1)

1. Detectamos Haulmer, Bsale, Nubox…

![Widget: usuario de Haulmer](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/04-mercado-usuario.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=b55a5c913fd44c9a2e0d419b56b7dbfb)

2. Usuario y clave del facturador — no la del SII.

![Widget: respaldar y migrar](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/05-mercado-migrar.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=65991ee468be3ce9b7e0e00a3062e7be)

3. Respaldo XML/PDF y migración a Tupana: 1 día hábil, aviso por correo.

**Banco**

![Widget: selector de banco](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/06-banco.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=687e9cf72a86d3e2c68c0489128dcbe6)

1. Elige el banco.

![Widget: clave del banco](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/07-banco-clave.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=1f637a596d120b07edd2ae267de46212)

2. RUT de la persona y clave.

![Widget: cuentas conectadas](https://mintcdn.com/tupana/qIPLgEE43o4mTOY0/images/connect/08-cuentas.png?fit=max\&auto=format\&n=qIPLgEE43o4mTOY0\&q=85\&s=0ce19592b74adca05f174e08aebe80ba)

3. El banco aceptó la clave: cuentas y saldos. Ahí sale success.

## Cuándo el enrolamiento es exitoso

El widget no avisa éxito al recibir la clave. Se queda en pantalla
mientras la institución responde. Si la clave está mala, el error queda
adentro y el usuario reintenta: no hay `success`. `success` sale cuando el
flujo terminó; `exit` sale solo si la persona cierra antes.

El `postMessage` mueve tu modal. El webhook
dice si la credencial quedó.

| Qué ves | Qué significa |
| - | - |
| `success` | El flujo terminó. Cierra el modal y guarda el `connection_id` |
| `exit` | Cerró antes de terminar. No hay conexión nueva |
| `connection.active` | La institución aceptó la clave. Esta es la confirmación |
| `connection.requires_action` | La clave no sirvió. El widget deja reintentar |

### SII

El login no termina en el request. La respuesta trae `status: "connecting"`
y un `enrollment_id` (`cenr_…`). El widget espera en
`ws/connect/{client_token}/` y manda `success` solo con `valid`.

```json theme={null}
{
  "enrollment_id": "cenr_8420193847261",
  "company_id": "ment_4829301923847",
  "status": "connecting"
}
```

Puedes esperar el webhook o consultar
`GET /api/v2/connections/enrollments/{enrollment_id}/` hasta `valid`,
`invalid` o `error`.

### Banco

El widget no manda `success` cuando guarda la clave. Se queda en
“estamos leyendo tus cuentas” hasta que el banco contesta.

* El banco aceptó y hay cuentas: el widget las muestra y ahí manda
  `success` con el `cred_…`. Tu backend recibe `connection.active`.
* La clave es inválida: el error queda en el widget, el usuario puede
  corregirla, y no hay `success`. Tu backend recibe
  `connection.requires_action`.
* El usuario cierra antes: `exit`. Eso no confirma la conexión. Si el banco igual acepta la clave, llega `connection.active`.

Guardar la clave con estado interno `VALID` no es la confirmación. La
confirmación es `connection.active`, cuando el banco ya listó las cuentas.

Los movimientos no vienen en ese aviso. Cuando la cartola está sincronizada
se leen como documentos tipo 100:
`GET /api/v2/documents/?master_entity_id={empresa}&dte_type__code__in=100`.

Responde 2xx al webhook y procesa después. Puede llegarte dos veces.

Para ver o cortar conexiones usa
[`GET /connections/`](https://docs.tupana.ai/api-reference/v2/connections/list) y
[`DELETE /connections/{connection_id}/`](https://docs.tupana.ai/api-reference/v2/connections/delete),
que la deja `inactive` y suelta la clave.

## Qué viaja por tu app

| | Tu aplicación | Tupana |
| - | - | - |
| Clave del SII / facturador / banco | Nunca | Cifrada |
| Credencial (`cred_…`) | Sí | Sí |
| Documentos, movimientos, registros | Sí | Sí |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.