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).
/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.
El tenant se deriva de la key.
{ "error": { "code": "VALIDATION", "message": "Parámetros inválidos.",
"details": { "amount": ["The amount must be greater than 0."] } } }
| code | HTTP | Cuándo |
|---|---|---|
VALIDATION | 422 | Parámetros inválidos (details por campo) |
LINE_NOT_FOUND | 404 | Línea inexistente |
NOT_FOUND | 404 | Solicitud/usuario inexistente |
CONFLICT | 409 | Estado no finalizable / máximo de solicitudes |
BIOMETRIC_PENDING | 422 | Biometría no completada |
PROVIDER_ERROR | 502 | Error del core de préstamo externo |
Líneas de crédito
{ "credit_lines": [
{ "id": 1, "name": "PERSONAL_01", "currency": "ARS", "min_installments": 3, "max_installments": 12 }
] }
Catálogos del formulario
{ "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 } ]
} }
Simulación (con fechas y cargos)
{ "amount": 100000, "installments": 12, "line_id": 1,
"disbursement_date": "2026-07-10", "first_due_date": "2026-08-10" }
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
amount | number | sí | Capital |
installments | integer | sí | Cuotas |
line_id / line_name | int/string | uno | Línea (id o nombre) |
disbursement_date | date | no | Base para los vencimientos. Default: hoy |
first_due_date | date | no | Fuerza el primer vencimiento |
{ "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.
Elegibilidad (riesgo)
{ "eligibility": { "decision": "approved",
"offer": { "amount": 100000, "installments": 12, "cuota": 9650.00 } } }
Variante comercios: GET /v2/loan/check-eligibility-comercio (+ credit-line-name).
Alta de solicitud
{ "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" }
{ "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"
} }
Estado de la solicitud
{ "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" } }
Finalización (liquidación)
Requiere biometría completada.
{ "application": { "id": 123, "status": "liquidada", "loan_number": "LN-778812", "next_step": null } }
409CONFLICT
422BIOMETRIC_PENDING
502PROVIDER_ERROR
- catalog + form-data
- simulate → cuota / cronograma
- check-eligibility → decisión + oferta
- apply →
next_step = BIOMETRIC_VERIFICATION - Verificación biométrica (redirección)
- status →
biometric_status = completed - finalize →
loan_number
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.
¿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.*).
| Campo | Descripción |
|---|---|
url | URL a la que se hace POST |
secret | Secreto compartido (HMAC-SHA256) |
events | Eventos suscritos (con comodines) |
active | On/off |
¿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 } }
| Campo | Descripción |
|---|---|
id | Id único (idempotencia) |
type | Tipo de evento |
occurred_at | ISO 8601 (UTC) |
data | Payload; depende del type |
Catálogo de eventos
| type | Cuándo |
|---|---|
application.created | Alta de solicitud |
application.eligibility.completed | Riesgo resuelto |
application.simulated | Simulación |
application.biometric.completed | Biometría |
application.signature.completed | Firma |
application.finalized | Liquidado |
application.rejected | Rechazado |
payment.received | Pago de cuota |
Ejemplos por evento
Verificación de firma y reintentos
// PHP
hash_equals(hash_hmac('sha256', $body, $secret), $signatureHeader);
5xx, 429 o sin respuesta: backoff exponencial (60s, 120s, 240s… tope 3600s), hasta 5 intentos. 4xx (salvo 429) definitivo. Deduplicar por id.