Connect
Conecta a tus clientes

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

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 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, 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. 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): 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

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; 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, 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

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

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

TopeValor
Cupo de trabajos pendientes por organizaciónSi se supera, 429 too_many_pending
Períodos por petición de backfill24
Trabajos simultáneos por conexión1, y uno solo activo por período
Enlaces de conexión por hora, por organización60

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 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). 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 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, conexiones.conexion.deshabilitar, conexiones.conexion.habilitar y 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 y te preparamos una propuesta.

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.

En esta página