Saltar al contenido principal

Inicio rápido

Próximamente

Todavía no hay sandbox ni URL pública, y las operaciones de negocio aún no responden de punta a punta. Puedes preparar las llaves, el registro y el código del token desde hoy; las respuestas mostradas son las del contrato, con datos ficticios.

Al terminar esta guía tendrás un token de acceso y sabrás leer un cliente, sus cuentas y un saldo, y hacer tu primera escritura idempotente.

1. Genera tu par de llaves​

La API no usa contraseñas: te autenticas firmando con tu llave privada, que nunca sale de tu sistema. FINORA solo guarda la llave pública.

# Llave privada EC P-256 (ES256) en PKCS#8. Guárdala en tu bóveda de secretos.
openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt -out privada.pem
# Llave pública para el registro
openssl ec -in privada.pem -pubout -out publica.pem

Convierte la pública a JWK con un kid que elijas:

import crypto from 'node:crypto';
import fs from 'node:fs';

const jwk = crypto.createPublicKey(fs.readFileSync('publica.pem')).export({ format: 'jwk' });
console.log(JSON.stringify({ keys: [{ ...jwk, kid: 'erp-2026-10', use: 'sig', alg: 'ES256' }] }, null, 2));

2. Regístrate como cliente​

Envía al emisor tu JWKS público (o una URL https donde lo publiques). A cambio recibes:

  • tu client_id;
  • los alcances concedidos (por ejemplo, customers:read accounts:read balances:read cards:block);
  • los productos a los que tienes acceso (solo verás objetos de esos productos);
  • tus límites de tasa.

3. Pide un token​

Firma una aserción JWT de vida corta y cámbiala por un token:

import crypto from 'node:crypto';
import fs from 'node:fs';

const b64url = (x) => Buffer.from(typeof x === 'string' ? x : JSON.stringify(x)).toString('base64url');
const TOKEN_URL = 'https://api-cms.example.com/v1/oauth/token';
const CLIENT_ID = 'erp-demo';

export function asercion() {
const ahora = Math.floor(Date.now() / 1000);
const cabecera = { alg: 'ES256', kid: 'erp-2026-10', typ: 'JWT' };
const claims = {
iss: CLIENT_ID,
sub: CLIENT_ID,
aud: TOKEN_URL,
iat: ahora,
exp: ahora + 120, // como máximo 5 minutos
jti: crypto.randomUUID(), // un solo uso
};
const entrada = `${b64url(cabecera)}.${b64url(claims)}`;
const firma = crypto.sign('sha256', Buffer.from(entrada), {
key: crypto.createPrivateKey(fs.readFileSync('privada.pem')),
dsaEncoding: 'ieee-p1363', // R‖S de 64 bytes, como exige JOSE
});
return `${entrada}.${firma.toString('base64url')}`;
}
curl https://api-cms.example.com/v1/oauth/token \
-d grant_type=client_credentials \
-d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
--data-urlencode "client_assertion=$ASERCION" \
--data-urlencode 'scope=customers:read accounts:read balances:read'
{
"access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6ImdhdGV3YXktMjAyNi0xMCJ9.e30.firma",
"token_type": "Bearer",
"expires_in": 300,
"scope": "accounts:read balances:read customers:read"
}

El token vive 5 minutos. Pide uno nuevo poco antes de que venza, siempre con una aserción nueva (jti distinto). Más detalle en Autenticación y alcances.

4. Lee un cliente, sus cuentas y un saldo​

La búsqueda de clientes es exacta por documento: prefijo de tipo (V, E, J, P o G) y número, sin separadores.

curl 'https://api-cms.example.com/v1/customers?documento=V12345678' \
-H "Authorization: Bearer $TOKEN"
{
"items": [
{
"customerId": "6f1d2c3b-8a4e-4c1f-9b2a-1c2d3e4f5a60",
"tipoDocumento": "V",
"documento": "12345678",
"nombre": "TITULAR DEMO",
"estado": "ACTIVO",
"creadoEn": "2026-09-02T10:15:00-04:00"
}
],
"siguiente": null
}
curl https://api-cms.example.com/v1/customers/6f1d2c3b-8a4e-4c1f-9b2a-1c2d3e4f5a60/accounts \
-H "Authorization: Bearer $TOKEN"

curl https://api-cms.example.com/v1/accounts/8a2b4c6d-1e3f-4a5b-9c7d-2e4f6a8b0c13/balance \
-H "Authorization: Bearer $TOKEN"

Saldo de una cuenta prepago:

{
"accountId": "8a2b4c6d-1e3f-4a5b-9c7d-2e4f6a8b0c13",
"asOf": "2026-10-20T14:05:11-04:00",
"producto": { "codigo": "210", "nombre": "PREPAGO DEMO", "familia": "PREPAGO", "regimen": "PREPAGO", "referencia": null },
"referencia": null,
"contrato": { "unidad": "VES", "asOf": "2026-10-20T14:05:11-04:00", "linea": null, "deuda": null, "retenido": "25.00", "pendienteConversion": null, "disponible": "1475.00" },
"valoracion": { "moneda": "VES", "saldo": "1500.00", "linea": null, "deuda": null, "retenido": "25.00", "disponible": "1475.00", "indice": null, "montoParaCancelarHoy": null },
"avisos": []
}

En crédito indexado en UVC, contrato está en UVC y valoracion en Bs: consulta la referencia para el ejemplo completo.

5. Haz tu primera escritura​

Toda escritura lleva una Idempotency-Key (UUID) nueva por operación lógica. Por ejemplo, el bloqueo temporal de un plástico (alcance cards:block):

curl https://api-cms.example.com/v1/cards/9b1f0e2d-3c4b-4a59-8e7f-6a5b4c3d2e10/block \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $(uuidgen)" \
-H 'X-Finora-Operador-Final: operador-17' \
-d '{"motivo":"SOLICITUD_TITULAR","detalle":"Bloqueo pedido por el titular en la app."}'
{
"cardId": "9b1f0e2d-3c4b-4a59-8e7f-6a5b4c3d2e10",
"accion": "BLOQUEO",
"estado": "BLOQUEADA_TEMPORAL",
"accionId": "6b5a4938-2716-4f05-8e4d-3c2b1a0f9e8d",
"eventoId": "e4a1b2c3-d4e5-4f60-8a7b-9c0d1e2f3a4b",
"ocurrioEn": "2026-10-20T14:30:00-04:00"
}

X-Finora-Operador-Final identifica a la persona o sistema de tu lado que pidió la acción; el CMS lo registra en su bitácora. Si falta, se registra tu client_id.

Si la red falla y no sabes si el bloqueo se aplicó, repite la misma petición con la misma clave: obtendrás la misma respuesta sin repetir la acción. Ver Idempotencia.

Siguientes pasos​