Referencia interna · API del Core

API del Core — Préstamos v2

Endpoints para operar una solicitud de punta a punta. v2 usa el mismo estándar profesional que la API de clientes externos (código HTTP + recurso limpio, errores estructurados) y corre sobre el loan-core (fechas y cargos por cuota).

Versión v2 Prefijo /api/v2/loan Auth X-Api-Key Server-to-server
v1 (/api/v1/loan) queda como legacy (envelope {status,data}), sin cambios. Integraciones nuevas → v2.

Estándar de respuestas y errores

Éxito = HTTP 2xx + recurso limpio en el body (sin envelope). Error = HTTP 4xx/5xx + { error: { code, message, details? } }. El resultado se comunica por el código HTTP, no por un campo status.

Autenticación
X-Api-Key: <clave-del-tenant>

El tenant se deriva de la key.

Formato de error
{ "error": { "code": "VALIDATION", "message": "Parámetros inválidos.",
  "details": { "amount": ["The amount must be greater than 0."] } } }
codeHTTPCuándo
VALIDATION422Parámetros inválidos (details por campo)
LINE_NOT_FOUND404Línea inexistente
NOT_FOUND404Solicitud/usuario inexistente
CONFLICT409Estado no finalizable / máximo de solicitudes
BIOMETRIC_PENDING422Biometría no completada
PROVIDER_ERROR502Error del core de préstamo externo
GET/v2/loan/catalog

Líneas de crédito

200Response
{ "credit_lines": [
  { "id": 1, "name": "PERSONAL_01", "currency": "ARS", "min_installments": 3, "max_installments": 12 }
] }
GET/v2/loan/form-data

Catálogos del formulario

200Response
{ "form_data": {
  "provinces": [ { "id": 1, "name": "Santa Fe" } ],
  "civil_statuses": [ { "id": 1, "name": "Soltero/a" } ],
  "employment_types": [ { "id": 1, "name": "Relación de dependencia" } ],
  "loan_reasons": [ { "id": 1, "name": "Consumo" } ],
  "credit_lines": [ { "id": 1, "name": "PERSONAL_01", "currency": "ARS", "min_installments": 3, "max_installments": 12 } ]
} }
POST/v2/loan/simulate

Simulación (con fechas y cargos)

Request
{ "amount": 100000, "installments": 12, "line_id": 1,
  "disbursement_date": "2026-07-10", "first_due_date": "2026-08-10" }
CampoTipoReq.Descripción
amountnumberCapital
installmentsintegerCuotas
line_id / line_nameint/stringunoLínea (id o nombre)
disbursement_datedatenoBase para los vencimientos. Default: hoy
first_due_datedatenoFuerza el primer vencimiento
200Response
{ "quote": {
  "currency": "ARS", "amount": 100000, "term": 12,
  "total_interest": 29000.00, "total_fees": 1800.00,
  "schedule": [
    { "number": 1, "due_date": "2026-08-10", "principal": 7000.00, "interest": 2500.00, "fees": 150.00, "amount": 9650.00, "balance": 93000.00 }
  ]
} }

Cada cuota: number, due_date, principal, interest, fees, amount (=principal+interest+fees), balance.

GET/v2/loan/check-eligibility

Elegibilidad (riesgo)

GET /v2/loan/check-eligibility?cuit=20304050609&amount=100000
200Response
{ "eligibility": { "decision": "approved",
  "offer": { "amount": 100000, "installments": 12, "cuota": 9650.00 } } }

Variante comercios: GET /v2/loan/check-eligibility-comercio (+ credit-line-name).

POST/v2/loan/apply

Alta de solicitud

Request (principales)
{ "dni": "20304050", "cuit": "20203040509", "first_name": "Juan", "last_name": "Pérez",
  "email": "juan@example.com", "phone": "5491133334444", "birth_date": "1990-05-12", "gender": "M",
  "civil_status_id": 1, "province_id": 1, "city": "Rosario", "street": "Av. Siempreviva",
  "street_number": "742", "postal_code": "2000", "cbu": "0000003100010000000001",
  "amount": 100000, "installments": 12, "line_name": "PERSONAL_01", "disbursement_date": "2026-07-10" }
201Response
{ "application": {
  "id": 123, "code": "004521", "status": "iniciada",
  "amount": 100000, "installments": 12, "installment_amount": 9650.00,
  "last_due_date": "2027-07-10", "loan_number": null,
  "biometric_status": null, "next_step": "BIOMETRIC_VERIFICATION"
} }
GET/v2/loan/{applicationId}/status

Estado de la solicitud

200Response
{ "application": { "id": 123, "code": "004521", "status": "iniciada",
  "amount": 100000, "installments": 12, "installment_amount": 9650.00,
  "last_due_date": "2027-07-10", "loan_number": null,
  "biometric_status": "pending", "next_step": "BIOMETRIC_VERIFICATION" } }

404{ error: { code: "NOT_FOUND" } }

POST/v2/loan/{applicationId}/finalize

Finalización (liquidación)

Requiere biometría completada.

200Response
{ "application": { "id": 123, "status": "liquidada", "loan_number": "LN-778812", "next_step": null } }

409CONFLICT   422BIOMETRIC_PENDING   502PROVIDER_ERROR

Flujo típico
  1. catalog + form-data
  2. simulate → cuota / cronograma
  3. check-eligibility → decisión + oferta
  4. applynext_step = BIOMETRIC_VERIFICATION
  5. Verificación biométrica (redirección)
  6. statusbiometric_status = completed
  7. finalizeloan_number
Adjunto: core-openapi.json (OpenAPI 3.1, v2).  ·  agiltech · uso interno
Referencia interna · Eventos salientes

Webhooks — Notificación de eventos

La plataforma avisa al suscriptor de cada paso relevante de la solicitud con un POST a una URL configurada. Evita el polling.

Transporte HTTP POST Firma HMAC-SHA256 Reintentos backoff Idempotencia por id

¿Dónde se configura la URL?

Por tenant, desde el backoffice, en la tabla webhook_endpoints. Varias URLs por tenant, cada una suscrita a distintos eventos (comodines * / application.*).

CampoDescripción
urlURL a la que se hace POST
secretSecreto compartido (HMAC-SHA256)
eventsEventos suscritos (con comodines)
activeOn/off
POSThttps://suscriptor/webhooks/…

¿Qué se envía?

POST https://cliente.com/webhooks/agiltech
X-Webhook-Id: evt_7f3c1a90
X-Webhook-Event: application.finalized
X-Webhook-Signature: 9a1f…   (HMAC-SHA256 hex del cuerpo crudo)

{ "id": "evt_7f3c1a90", "type": "application.finalized", "occurred_at": "2026-07-10T14:32:05Z",
  "data": { "application_id": 123, "loan_number": "LN-778812", "amount": 100000, "installments": 12 } }
CampoDescripción
idId único (idempotencia)
typeTipo de evento
occurred_atISO 8601 (UTC)
dataPayload; depende del type

Catálogo de eventos

typeCuándo
application.createdAlta de solicitud
application.eligibility.completedRiesgo resuelto
application.simulatedSimulación
application.biometric.completedBiometría
application.signature.completedFirma
application.finalizedLiquidado
application.rejectedRechazado
payment.receivedPago de cuota

Ejemplos por evento

Verificación de firma y reintentos

// PHP
hash_equals(hash_hmac('sha256', $body, $secret), $signatureHeader);
Reintentos. Responder 2xx. Ante 5xx, 429 o sin respuesta: backoff exponencial (60s, 120s, 240s… tope 3600s), hasta 5 intentos. 4xx (salvo 429) definitivo. Deduplicar por id.