# El caso plataforma

> Para un producto que conecta a muchos clientes finales, el modelo de datos, el alta embebida y los topes que importan a escala.



Si tu producto (un ERP, un software de gestión, una plataforma vertical) necesita leer el SII o los
bancos de muchos clientes finales, este es tu mapa. El mecanismo es el mismo de
[Conecta tu primera empresa](/docs/empezar/conectar); lo que cambia a escala es el modelo de datos,
cómo identificas a cada cliente y contra qué topes vas a operar.

## El modelo: dos niveles, no tres [#el-modelo-dos-niveles-no-tres]

```text
organización (org_…)        tu cuenta. Aquí viven tus API keys y tu facturación.
  └── conexión (conn_…)     un cliente tuyo en un sistema. La unidad de todo lo demás.
```

No existe una entidad «proyecto». Si vienes de un modelo cuenta → proyecto → enlace, el equivalente del
proyecto es la conexión, y el enlace de un solo uso es solo el permiso para entregar la credencial:
una vez consumido, lo que queda es la conexión. Una organización puede tener tantas conexiones del
mismo sistema como clientes tengas; toda llamada a sus tools debe nombrar cuál con
`X-Connect-Connection`, incluso con una sola conexión (omitirlo responde `validation_error`). El detalle del ciclo de vida está en [Conexiones](/docs/conceptos/conexiones).

## Identificar a tu cliente [#identificar-a-tu-cliente]

Las conexiones no tienen un campo `external_id` ni `metadata`. **El mapeo cliente ↔ `conn_…` lo guardas
tú**, y el momento de capturarlo es el alta: el `connection_id` llega en el `postMessage` del iframe
cuando la persona completa el enlace, y en el webhook `connect_session.consumed` si tienes
[webhooks](/docs/operar/webhooks) configurados. El webhook trae también el `session.id` que recibiste al
crear el enlace, así que puedes guardar el par sesión ↔ cliente desde ese momento.

Para reconocer cada conexión en el panel, en la bitácora y en los cobros, ponle nombre al crear el
enlace con `display_name` en [`POST /v1/connect_sessions`](/docs/operar/api-control#post-v1connect_sessions),
por ejemplo `"Comercial Andes SpA (SII)"`. Sin él, la conexión se llama como su sistema (`SII`, `SII 2`).
Las que ya existen se renombran con
[`conexiones.conexion.renombrar`](/docs/referencia/conexiones/conexion-renombrar). El nombre no se muestra
en la página del enlace. Para distinguir empresas por un dato que no depende del nombre,
`conexiones.estado.consultar` y `GET /v1/connections` traen el RUT de cada conexión cuando ya se conoce.

Por defecto una API key alcanza todas las conexiones de tu organización: con `sii:read` puede leer cualquier
conexión del SII. Para levantar muros entre clientes finales, acota cada clave a las conexiones de su cliente
([conexiones de una clave](/docs/empezar/autenticacion#conexiones-de-una-clave)): lo que queda fuera de su lista
responde como si no existiera. Una clave acotada no crea conexiones, así que el enlace de alta lo pide una clave con
acceso a todas, y la conexión nueva se agrega a la lista editando la clave.

Con Gmail esto pesa más: una clave con `gmail:readonly` lee todos los buzones que alcanza. Si conectas los buzones de
varios clientes finales, acota cada clave a los de su cliente. El enlace de Gmail no recoge una clave: abre el
consentimiento de Google fuera del iframe (en una ventana nueva, o en la misma pestaña si no hay iframe), y el
resultado te llega por el mismo `postMessage`. Si tu iframe usa
`sandbox`, incluye `allow-popups`; si no, la página ofrece abrir el enlace en una pestaña.

## El alta, dentro de tu producto [#el-alta-dentro-de-tu-producto]

`POST /v1/connect_sessions` con `allowed_origins` devuelve una URL de un solo uso que se monta en un
iframe de tu app: tu cliente escribe su clave en la pantalla de Emisso sin salir de tu producto, la
credencial viaja directo al vault y el resultado te vuelve por `postMessage`, por `redirect_uri` y
por webhook. El contrato completo (orígenes, TTL, intentos, el modelo de amenaza) está en
[el enlace hosted](/docs/plataformas/enlace-hosted); el endpoint, en la
[API de control](/docs/operar/api-control). Dos datos que importan a escala:

* La emisión de enlaces tiene tope por organización (60 por hora, `429` con `Retry-After`): un alta
  masiva de clientes se planifica por tandas.
* La primera sincronización queda encolada sola al conectar, tanto por el enlace como desde el
  dashboard, salvo que falle la comprobación de la credencial.

## Cuando la credencial de un cliente se rompe [#cuando-la-credencial-de-un-cliente-se-rompe]

Te enteras por tres señales, en este orden si tienes webhooks: llega `sync.failed`, las llamadas de
esa conexión empiezan a responder `connection_credential_required` (428, con la sugerencia del SII o
del banco en el mensaje), y, si persisten los rechazos de autenticación, la cadencia automática
se pausa y deja de encolar. La conexión sigue `active` y sus tools de consulta siguen
leyendo lo ya persistido; lo que se detiene es el reloj.

La reconexión es el mismo endpoint de enlaces con `"mode": "reauth"` y el `connection_id`: rota la
credencial sobre la misma conexión, sin duplicarla. Guardar la clave no demuestra que la conexión se haya recuperado: comprueba
la verificación y el trabajo que se encola. Una sincronización exitosa puede limpiar la pausa por
credencial y restaurar la cadencia anterior. Revisa `cadenciaEstado` antes de darla por reanudada;
no hace falta cambiar la frecuencia a mano.

## Operar la flota [#operar-la-flota]

Los topes que vas a tocar con decenas o cientos de conexiones, todos del código:

| Tope                                           | Valor                                |
| ---------------------------------------------- | ------------------------------------ |
| Cupo de trabajos pendientes por organización   | Si se supera, `429 too_many_pending` |
| Períodos por petición de backfill              | 24                                   |
| Trabajos simultáneos por conexión              | 1, y uno solo activo por período     |
| Enlaces de conexión por hora, por organización | 60                                   |

La duración depende de la carga, del conector y del portal externo; no hay un rendimiento fijo ni un
plazo garantizado. Reparte el backfill en tandas y conserva los `job_ids` que devuelve
`POST /v1/connections/{conn}/syncs`. Sigue cada uno con `GET /v1/syncs/{id}` o los webhooks `sync.*`.
`conexiones.estado.consultar` muestra solo los cinco últimos trabajos por conexión. No existe
`GET /v1/connections/{id}/estado`. El [ejemplo de dos meses](/docs/operar/api-control#pedir-agosto-y-septiembre-y-ver-su-estado)
muestra el pedido y seguimiento completos. Si es urgente, escríbenos desde el chat y lo revisamos;
no hay un parámetro de prioridad para clientes.

Sobre la cuota: **encolar no consume cuota**; lo que cuenta como llamada es cada sincronización
ejecutada y cada página de consulta (el detalle de categorías está en
[Límites y planes](/docs/operar/limites)). El cupo es **por conexión**, así que la aritmética de una
flota grande no se multiplica por el número de conexiones: cada una mide su propio período contra el
mismo techo, y agregar clientes no acerca a los demás al corte. Lo que hay que dimensionar por
conexión es sincronizaciones diarias × páginas leídas. Subir de plan no mueve ese techo; si tu
cadencia lo roza, escríbenos desde [connect.emisso.ai/billing](https://connect.emisso.ai/billing) y
lo ampliamos para tu organización.

## Lo que todavía se hace solo desde el dashboard [#lo-que-todavía-se-hace-solo-desde-el-dashboard]

Hoy no hay endpoint para crear organizaciones ni para acuñar o rotar API keys. Si
tu integración necesita alguna de esas operaciones por API, escríbenos: saber qué falta primero es lo
que ordena el roadmap.

Renombrar, deshabilitar, habilitar y eliminar una conexión sí se pueden hacer por API, con
[`conexiones.conexion.renombrar`](/docs/referencia/conexiones/conexion-renombrar),
[`conexiones.conexion.deshabilitar`](/docs/referencia/conexiones/conexion-deshabilitar),
[`conexiones.conexion.habilitar`](/docs/referencia/conexiones/conexion-habilitar) y
[`conexiones.conexion.eliminar`](/docs/referencia/conexiones/conexion-eliminar). Cada una exige su propio
permiso (`conexiones:rename`, `conexiones:disable`, `conexiones:enable` y `conexiones:delete`), y eliminar no se puede
deshacer: borra la credencial, los datos sincronizados, el historial y los webhooks de esa conexión.

La cadencia de sincronización no está en esa lista porque no es un pendiente: **no se configura en
ninguna parte**, ni por API ni desde el dashboard. La decide Emisso por conector, en los sistemas que sincronizan. Para forzar una sincronización puntual, el camino es
`sistema.conexion.sincronizar` (o `POST /v1/connections/{conn}/syncs`), que siguen disponibles. Si tu
producto necesita una frecuencia de actualización mayor que la programada, escríbenos a
[contacto@tryemisso.com](mailto:contacto@tryemisso.com) y te preparamos una propuesta.

## Próximos pasos [#próximos-pasos]

* [El enlace hosted](/docs/plataformas/enlace-hosted): el contrato completo del alta embebida.
* [API de control](/docs/operar/api-control): sesiones, sincronizaciones y webhooks, endpoint por endpoint.
* [Webhooks](/docs/operar/webhooks): enterarte de cada alta y cada sync sin polling.


---

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.
