FINORA Core · Tutorial
Tu primera llamada a la API del core
This content is not available in your language yet.
Al terminar habrás registrado y reversado un asiento contable desde la línea de comandos, y sabrás leer lo que el core
te responde en cada paso. Solo necesitas curl, jq y uuidgen.
Antes de empezar
Sección titulada «Antes de empezar»Pide a quien opera el gateway de tu institución:
- la URL del sandbox (con la forma
https://<institucion>.sandbox.<su-gateway>); - un
client_idy unclient_secretde sandbox con los alcancesgl:readygl:write; - dos cuentas postables de prueba y el código de una agencia.
Este tutorial usa datos ficticios del «Banco Demo»: la agencia AG-001 y tres cuentas del plan con forma SUDEBAN.
| Cuenta | Nombre | Clase |
|---|---|---|
1101010001 | Efectivo en caja, AG-001 | ASSET |
2810110001 | IVA por pagar | LIABILITY |
5110110001 | Comisiones por servicios | INCOME |
Guarda los datos en variables de tu terminal (nunca en un archivo del repositorio):
GW="https://<institucion>.sandbox.<su-gateway>"CLIENT_ID="<TU_CLIENT_ID>"CLIENT_SECRET="<TU_CLIENT_SECRET>"Los pasos
Sección titulada «Los pasos»-
Pide el token. Con la credencial en
Authorization: Basic:Ventana de terminal curl -s -u "$CLIENT_ID:$CLIENT_SECRET" -d grant_type=client_credentials "$GW/v1/oauth/token" | jqVerás algo así:
{"access_token": "<TOKEN>","token_type": "Bearer","expires_in": 900,"scope": "gl:read gl:write","tenant": "banco-demo-sandbox","environment": "sandbox"}Comprueba
environment: debe decirsandbox. El token dura 900 segundos; guárdalo:Ventana de terminal TOKEN=$(curl -s -u "$CLIENT_ID:$CLIENT_SECRET" -d grant_type=client_credentials "$GW/v1/oauth/token" | jq -r .access_token)Si recibes 401
GW_CREDENCIAL_INVALIDA, revisa el par; tras varios intentos fallidos la credencial se bloquea unos minutos (429). -
Lista el plan de cuentas. Las cuentas postables de activo, de diez en diez, con los totales en cabeceras:
Ventana de terminal curl -s -D - "$GW/v1/glaccounts?type=ASSET&postable=true&limit=10&paginationDetails=ON" \-H "Authorization: Bearer $TOKEN"En las cabeceras verás
Items-Total,Items-Offset: 0eItems-Limit: 10, yX-Finora-Environment: sandbox. El cuerpo es un arreglo:[{"glCode": "1101010001","name": "Efectivo en caja, AG-001","type": "ASSET","nature": "D","postable": true,"usage": "DETAIL","allowManualJournalEntries": true,"active": true,"currency": "VES","cia": "001"}]Antes de postear contra una cuenta, mira tres campos:
postable(recibe asientos),activeyallowManualJournalEntries. Si alguno esfalse, el core rechazará el asiento. -
Consulta el saldo de la caja.
balanceDatetrae el saldo acumulado hasta esa fecha, con signo por naturaleza (positivo = saldo normal):Ventana de terminal curl -s "$GW/v1/glaccounts/1101010001?detailsLevel=FULL" -H "Authorization: Bearer $TOKEN" | jq .balanceAnota el
balanceen VES: lo vas a ver cambiar. -
Registra un asiento de tres piernas. El cobro en caja de una comisión de 100,00 VES más 16,00 de IVA: un débito y dos créditos. Débitos y créditos deben sumar lo mismo. Genera la clave de idempotencia una sola vez y guárdala:
Ventana de terminal CLAVE=$(uuidgen)cat > asiento.json <<'EOF'{"currency": "VES","branchCode": "AG-001","notes": "Cobro de comisión por servicio, tutorial","transactionId": "tutorial-0001","debits": [ { "glCode": "1101010001", "amount": "116.00" } ],"credits": [ { "glCode": "5110110001", "amount": "100.00" },{ "glCode": "2810110001", "amount": "16.00" } ]}EOFcurl -s -i "$GW/v1/gljournalentries" -H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" -H "Idempotency-Key: $CLAVE" -d @asiento.jsonSin
date, el asiento toma la fecha operativa del core. VerásHTTP/1.1 201y el comprobante:{"voucher": "GL-20260919-3f9c2a7b1d4e","date": "2026-09-19","currency": "VES","branchCode": "AG-001","notes": "Cobro de comisión por servicio, tutorial","transactionId": "tutorial-0001","status": "DRAFT","entries": [{ "glCode": "1101010001", "type": "DEBIT", "amount": 116.00, "currency": "VES", "branchCode": "AG-001" },{ "glCode": "5110110001", "type": "CREDIT", "amount": 100.00, "currency": "VES", "branchCode": "AG-001" },{ "glCode": "2810110001", "type": "CREDIT", "amount": 16.00, "currency": "VES", "branchCode": "AG-001" }]}DRAFTquiere decir que las líneas ya están en el mayor y esperan el cierre del día, como todo asiento del core. Guarda el comprobante:Ventana de terminal VOUCHER="GL-20260919-3f9c2a7b1d4e" # el que te devolvió el core -
Repite la misma petición, con la misma clave. Es lo que haría tu sistema si se cortó la red:
Ventana de terminal curl -s -i "$GW/v1/gljournalentries" -H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" -H "Idempotency-Key: $CLAVE" -d @asiento.json | rg -i 'HTTP/|replayed|voucher'Verás el mismo
vouchery la cabeceraIdempotent-Replayed: true. No se creó un segundo asiento. Si cambias un importe y reusas la clave, el gateway responde 422IDEMPOTENCIA_CUERPO_DISTINTO. -
Busca tu asiento. La búsqueda devuelve líneas del mayor y siempre exige un rango de
bookingDateconBETWEEN. Combínalo con tutransactionId:Ventana de terminal curl -s -D - "$GW/v1/gljournalentries:search?limit=50&paginationDetails=ON" \-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"filterCriteria": [{ "field": "bookingDate", "operator": "BETWEEN", "value": "2026-09-01", "secondValue": "2026-09-30" },{ "field": "transactionId", "operator": "EQUALS", "value": "tutorial-0001" }],"sortingCriteria": { "field": "glCode", "order": "ASC" }}'Verás
Items-Total: 3y las tres líneas con el mismovoucher. Quita el criterio debookingDatey prueba: 400RANGO_DE_FECHAS_REQUERIDO, conerrorSource: filterCriteria. -
Revérsalo. Un asiento no se borra ni se edita: se reversa con un contra-asiento espejo. Usa una clave nueva:
Ventana de terminal curl -s -i -X POST "$GW/v1/gljournalentries/$VOUCHER:reverse" -H "Authorization: Bearer $TOKEN" \-H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" -d '{ "notes": "Reverso del tutorial" }'Verás 201 y un comprobante
REV-GL-20260919-3f9c2a7b1d4econreversalOfapuntando al original y las mismas cuentas con los lados invertidos. Si consultas el original (GET /v1/gljournalentries/$VOUCHER), ahora traereversedBy. Intenta reversarlo otra vez: 409CORE_JOURNAL_ENTRY_ALREADY_REVERSED.
Lo que acabas de hacer
Sección titulada «Lo que acabas de hacer»sequenceDiagram
participant T as Tu terminal
participant G as Gateway (sandbox)
participant K as Core del sandbox
T->>G: POST /v1/oauth/token
G-->>T: access_token (900 s)
T->>G: GET /v1/glaccounts
G->>K: plan de cuentas
T->>G: POST /v1/gljournalentries + Idempotency-Key
G->>K: asiento balanceado
K-->>T: 201 GL-…
T->>G: el mismo POST, la misma clave
G-->>T: 201 GL-… (Idempotent-Replayed)
T->>G: POST :search (bookingDate BETWEEN)
T->>G: POST /{voucher}:reverse
K-->>T: 201 REV-GL-…
Consultaste, escribiste con garantía de no duplicar, buscaste y corregiste por reverso: el ciclo completo de un sistema que contabiliza contra el core.
Si algo no salió
Sección titulada «Si algo no salió»| Lo que ves | Por qué | Qué hacer |
|---|---|---|
403 GW_ALCANCE_INSUFICIENTE en el paso 4 | Tu credencial no tiene gl:write | Pide el alcance al operador del gateway |
403 CORE_ACCESO_DENEGADO | El gateway te deja pasar, pero su credencial ante el core no puede postear | El administrador del core agrega gl:write a esa credencial |
422 CORE_UNBALANCED_ENTRY | Débitos y créditos no suman lo mismo | Revisa los importes |
422 CORE_GL_ACCOUNT_NOT_POSTABLE o CORE_GL_ACCOUNT_MANUAL_ENTRIES_NOT_ALLOWED | La cuenta es sumaria o no admite asientos manuales | Elige otra con postable y allowManualJournalEntries en true |
422 CORE_PERIOD_CLOSED o CORE_FUTURE_BOOKING_DATE | Mandaste una date de un día cerrado o posterior a la fecha operativa | Quita date o usa la fecha operativa |
422 CORE_MANUAL_CONCEPT_UNAVAILABLE | La institución no tiene el concepto de asientos manuales | Lo crea el contador del banco |
Para seguir
Sección titulada «Para seguir»- Referencia de la API contable: todos los campos, los criterios de búsqueda, el balance de comprobación y los reportes asíncronos.
- El gateway de API del core: por qué cada cabecera está ahí.
- Consumir el streaming de eventos: enterarte de cada asiento sin preguntar.