Conecta a tus clientes
El caso plataforma, un producto que conecta a muchos clientes finales, con 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; 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
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; con dos o más activas, toda llamada debe nombrar cuál con
X-Connect-Connection (omitirlo responde validation_error con la lista de candidatas en el
suggested_fix). El detalle del ciclo de vida está en Conexiones.
Identificar a tu cliente
Las conexiones no tienen un campo external_id ni metadata, y su display_name lo autogenera el
servidor (SII, SII 2). 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
configurados. Guarda ese par en tu base al recibirlo; después no hay endpoint para reconstruirlo
desde tu lado (solo GET /v1/connections, que lista ids y nombres autogenerados).
El aislamiento es por organización, no por conexión: una API key con sii:read puede leer cualquier
conexión del SII de tu organización, y no se puede acuñar una clave limitada a una sola conexión. Si
tu arquitectura exige muros entre clientes finales, el muro lo pones tú delante de Connect.
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 Connect 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; el endpoint, en la
API de control. Dos datos que importan a escala:
- La emisión de enlaces tiene tope por organización (60 por hora,
429conRetry-After): un alta masiva de clientes se planifica por tandas. - Por el carril del enlace, la primera sincronización queda encolada sola al conectar. Por el dashboard no: ahí el alta solo verifica la credencial.
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 al tercer fallo de autenticación consecutivo la cadencia automática de esa
conexión se pausa sola 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. Un detalle operativo: el enlace reauth no
reanuda la cadencia pausada; eso hoy se hace desde el dashboard (rotar la credencial o verificar la
conexión desde su pestaña de ajustes limpia la pausa y restaura la cadencia anterior).
Operar la flota
Los topes que vas a tocar con decenas o cientos de conexiones, todos del código:
| Tope | Valor |
|---|---|
| Trabajos de sincronización encolados más corriendo, por organización | 50 (429 too_many_pending) |
| Períodos por petición de backfill | 24 |
| Ritmo de drenaje de la cola | hasta 10 trabajos cada 5 minutos |
| Trabajos simultáneos por conexión | 1, y uno solo activo por período |
| Enlaces de conexión por hora, por organización | 60 |
El ritmo de drenaje define tu techo de sincronización: alrededor de 120 trabajos por hora. Un
backfill grande de muchos clientes se reparte en tandas y se supervisa con los webhooks sync.* o
con GET /v1/syncs/{id}, no reencolando a ciegas.
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). 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 y lo ampliamos para tu organización.
Lo que todavía se hace solo desde el dashboard
Hoy no hay endpoint para crear organizaciones, acuñar o rotar API keys, renombrar, deshabilitar o eliminar una conexión, ni configurar la cadencia de sincronización. Si tu integración necesita alguna de esas operaciones por API, escríbenos: saber qué falta primero es lo que ordena el roadmap.
Próximos pasos
- El enlace hosted: el contrato completo del alta embebida.
- API de control: sesiones, sincronizaciones y webhooks, endpoint por endpoint.
- Webhooks: enterarte de cada alta y cada sync sin polling.
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.
Webhooks
Un aviso firmado cuando una sincronización termina o un enlace de conexión se usa, sin sondear. El webhook nunca es load-bearing: el estado real siempre se puede consultar.