Ir al contenido
FINORA Academy
SistemaFINORA Core

FINORA Core · Tutorial

Tu primera llamada a la API del core

ImplementadoImplementadoBásico25 min

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.

Pide a quien opera el gateway de tu institución:

  • la URL del sandbox (con la forma https://<institucion>.sandbox.<su-gateway>);
  • un client_id y un client_secret de sandbox con los alcances gl:read y gl: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.

CuentaNombreClase
1101010001Efectivo en caja, AG-001ASSET
2810110001IVA por pagarLIABILITY
5110110001Comisiones por serviciosINCOME

Guarda los datos en variables de tu terminal (nunca en un archivo del repositorio):

Ventana de terminal
GW="https://<institucion>.sandbox.<su-gateway>"
CLIENT_ID="<TU_CLIENT_ID>"
CLIENT_SECRET="<TU_CLIENT_SECRET>"
  1. 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" | jq

    Verá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 decir sandbox. 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).

  2. 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: 0 e Items-Limit: 10, y X-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), active y allowManualJournalEntries. Si alguno es false, el core rechazará el asiento.

  3. Consulta el saldo de la caja. balanceDate trae 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 .balance

    Anota el balance en VES: lo vas a ver cambiar.

  4. 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" } ]
    }
    EOF
    curl -s -i "$GW/v1/gljournalentries" -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" -H "Idempotency-Key: $CLAVE" -d @asiento.json

    Sin date, el asiento toma la fecha operativa del core. Verás HTTP/1.1 201 y 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" }
    ]
    }

    DRAFT quiere 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
  5. 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 voucher y la cabecera Idempotent-Replayed: true. No se creó un segundo asiento. Si cambias un importe y reusas la clave, el gateway responde 422 IDEMPOTENCIA_CUERPO_DISTINTO.

  6. Busca tu asiento. La búsqueda devuelve líneas del mayor y siempre exige un rango de bookingDate con BETWEEN. Combínalo con tu transactionId:

    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: 3 y las tres líneas con el mismo voucher. Quita el criterio de bookingDate y prueba: 400 RANGO_DE_FECHAS_REQUERIDO, con errorSource: filterCriteria.

  7. 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-3f9c2a7b1d4e con reversalOf apuntando al original y las mismas cuentas con los lados invertidos. Si consultas el original (GET /v1/gljournalentries/$VOUCHER), ahora trae reversedBy. Intenta reversarlo otra vez: 409 CORE_JOURNAL_ENTRY_ALREADY_REVERSED.

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.

Lo que vesPor quéQué hacer
403 GW_ALCANCE_INSUFICIENTE en el paso 4Tu credencial no tiene gl:writePide el alcance al operador del gateway
403 CORE_ACCESO_DENEGADOEl gateway te deja pasar, pero su credencial ante el core no puede postearEl administrador del core agrega gl:write a esa credencial
422 CORE_UNBALANCED_ENTRYDébitos y créditos no suman lo mismoRevisa los importes
422 CORE_GL_ACCOUNT_NOT_POSTABLE o CORE_GL_ACCOUNT_MANUAL_ENTRIES_NOT_ALLOWEDLa cuenta es sumaria o no admite asientos manualesElige otra con postable y allowManualJournalEntries en true
422 CORE_PERIOD_CLOSED o CORE_FUTURE_BOOKING_DATEMandaste una date de un día cerrado o posterior a la fecha operativaQuita date o usa la fecha operativa
422 CORE_MANUAL_CONCEPT_UNAVAILABLELa institución no tiene el concepto de asientos manualesLo crea el contador del banco