FINORA Core · Guía
Consumir el streaming de eventos del core
Cómo funciona
Sección titulada «Cómo funciona»Cada operación del core escribe su evento en el registro de eventos en la misma transacción. El streaming te lo entrega en orden y con una posición (el cursor). Guardando el último cursor que procesaste, puedes detener tu consumidor una hora o un día y retomar exactamente donde quedaste.
flowchart LR
subgraph Core
OB[("Registro de eventos<br/>orden estable")]
end
C["Tu consumidor"] -->|"1. leer desde mi cursor"| OB
OB -->|"2. lote + cursor del último"| C
C -->|"3. procesar"| DB[("Tu base")]
C -->|"4. confirmar cursor"| OB
Tres reglas que conviene tener claras:
- Orden estable. Los eventos salen ordenados por la transacción que los creó y su orden dentro de ella, y solo de transacciones ya confirmadas: un evento que confirma tarde no aparece «por detrás» de un cursor que ya entregaste.
- Al menos una vez. Si tu consumidor cae entre procesar y confirmar, recibirás otra vez esos eventos. Deduplica por
eid, el identificador estable de cada evento. - El cursor es opaco. Tiene la forma
48213-1052, pero no le busques significado ni lo construyas: úsalo tal como llega.BEGIN(o vacío) es antes del primer evento conservado.
Los tipos de evento
Sección titulada «Los tipos de evento»| Tipo | Qué anuncia | metadata.aggregateType |
|---|---|---|
AccEntryPosted | Un asiento contabilizado | AccEntry |
TransactionPosted | Una transacción de cliente | DatTransaction |
AccountChanged | Una cuenta de cliente creada o cambiada | la cuenta |
CustomerChanged | Un cliente creado o cambiado | el cliente |
Dos formas de leer
Sección titulada «Dos formas de leer»| Lectura libre | Suscripción | |
|---|---|---|
| Ruta | GET /v1/events?cursor&limit&eventTypes | GET /v1/subscriptions/{id}/events?limit |
| Dónde vive tu cursor | En tu sistema | En el core |
| Para | Una lectura puntual, una carga inicial, depurar | Un consumidor permanente |
Ambas exigen el alcance events:read. En el core es un permiso crítico, porque saca eventos del banco en masa: la
credencial del gateway ante el core lo necesita de forma expresa.
Lectura libre
Sección titulada «Lectura libre»curl -s -D - "$GW/v1/events?cursor=BEGIN&limit=100&eventTypes=AccEntryPosted,TransactionPosted" \ -H "Authorization: Bearer $TOKEN"limit va de 1 a 1000 (100 por defecto). La respuesta:
{ "cursor": "48213-1052", "events": [ { "eid": "0b7f3d2a-6c1e-4f8a-9d2b-5e4c3a1f0e9d", "cursor": "48213-1052", "occurredAt": "2026-09-19T14:05:12.481Z", "eventType": "AccEntryPosted", "metadata": { "aggregateType": "AccEntry", "aggregateId": "5a2e9c1b-3d4f-4a6b-8c7d-9e0f1a2b3c4d" }, "body": { "eventType": "AccEntryPosted", "eventVersion": "1", "payload": { "currency": "VES", "baseAmount": "116.00" } } } ]}El cursor de arriba es el del último evento del lote; el gateway lo copia también en la cabecera Items-Next-Cursor.
Para el siguiente lote, pásalo en ?cursor=. Con el lote vacío, el cursor no cambia: espera unos segundos y vuelve a
leer.
Un consumidor con suscripción
Sección titulada «Un consumidor con suscripción»-
Crea la suscripción, una vez:
Ventana de terminal curl -s -i "$GW/v1/subscriptions" -H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" -d '{"owningApplication": "conciliador-banco-demo","consumerGroup": "default","eventTypes": ["AccEntryPosted"],"readFrom": "begin"}'Verás
201con elid. La suscripción es única por aplicación, grupo y tipos de evento: repetir la misma petición devuelve200con la existente, así que tu consumidor puede crearla al arrancar sin miedo.readFromesbegin(por defecto, desde el primer evento conservado) oend(solo lo que ocurra desde ahora). -
Lee un lote. Siempre empieza en el último cursor confirmado; no se pasa
cursor(si lo pasas: 400CURSOR_NO_ADMITIDO):Ventana de terminal curl -s "$GW/v1/subscriptions/<ID>/events?limit=200" -H "Authorization: Bearer $TOKEN"Mientras no confirmes, la misma llamada devuelve los mismos eventos.
-
Procesa el lote y guarda en tu base, en la misma transacción, los
eidque procesaste. -
Confirma el cursor del lote:
Ventana de terminal curl -s "$GW/v1/subscriptions/<ID>/cursors" -H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" -d '{ "cursor": "48213-1052" }'Respuesta:
{"committed": true, "committedCursor": "48213-1052"}. Confirmar un cursor igual o anterior al ya confirmado responde200concommitted: false: no es un error, la confirmación es idempotente. -
Repite desde el paso 2. Con el lote vacío, espera unos segundos antes de volver a leer.
// Consumidor mínimo (Node 18+). TOKEN se renueva antes de sus 900 segundos.async function consumir(gw, token, id, procesar) { const h = { Authorization: `Bearer ${token()}`, 'Content-Type': 'application/json' }; for (;;) { const lote = await (await fetch(`${gw}/v1/subscriptions/${id}/events?limit=200`, { headers: h })).json(); if (lote.events.length === 0) { await new Promise((r) => setTimeout(r, 5000)); continue; } for (const e of lote.events) await procesar(e); // idempotente por e.eid await fetch(`${gw}/v1/subscriptions/${id}/cursors`, { method: 'POST', headers: h, body: JSON.stringify({ cursor: lote.cursor }), }); }}Si algo falla
Sección titulada «Si algo falla»| Lo que ves | Por qué | Qué hacer |
|---|---|---|
400 CURSOR_INVALIDO | El cursor no tiene el formato que entregó el core | Usa el cursor tal como llegó |
422 CORE_CURSOR_AHEAD_OF_STREAM | Confirmaste un cursor posterior al último evento disponible | Confirma solo cursores que recibiste |
409 CORE_CONCURRENT_COMMIT | Dos procesos confirmaron a la vez sobre la misma suscripción | Un solo consumidor por suscripción; reintenta |
404 CORE_EVENT_SUBSCRIPTION_NOT_FOUND | La suscripción fue borrada | Créala de nuevo; empezará según su readFrom |
| Lotes siempre vacíos | Registro de eventos apagado, o ningún evento de esos tipos | Pregunta al administrador del core |
En la consola del core
Sección titulada «En la consola del core»Administración → Suscripciones de eventos lista los consumidores y el último cursor que confirmó cada uno. Borrar una suscripción hace que su consumidor empiece de nuevo: hazlo solo si eso es lo que quieres.
Para seguir
Sección titulada «Para seguir»- Webhooks firmados del core, si prefieres que el core te avise.
- El gateway de API del core.