Ir al contenido

Postbacks de estado

Para: Lenders (equipo de integración)

Cuando un consumidor solicita uno de tus productos a través de Prosperas, vos procesás esa solicitud de tu lado (la aprobás, desembolsás los fondos o la rechazás). Un postback es la forma en que le contás a Prosperas qué pasó, casi en tiempo real, para que podamos actualizar el estado del lead, conciliar el revenue y mantener sincronizada la experiencia del consumidor.

La integración es deliberadamente chica: un endpoint, una credencial, un formato de payload.

Propiedad Valor
Dirección Vos → Prosperas (vos nos llamás)
Endpoint POST {BASE_URL}/client_app/webhook/lender/loan-status (existe una variante GET equivalente)
Autenticación Header X-API-KEY (recomendado) o IP allowlist
Formato del body JSON, snake_case
Eventos application, approval, disbursement, rejected, no_payment_yet, on_time, late, default, paid_in_full
Éxito 200 OK con un acknowledgement en JSON
Reintentos Vos reintentás ante 5xx / errores de red (nosotros no te reintentamos)

Todo postback tiene que autenticarse. Prosperas revisa primero la API key, y después la IP de origen. Si ninguna de las dos coincide con una credencial registrada, la solicitud se rechaza con 403.

Enviá tu key en el header HTTP:

X-API-KEY: <tu-key>
  • El nombre del header no distingue mayúsculas/minúsculas (X-API-KEY, x-api-key, X-Api-Key funcionan igual).
  • La key se valida por coincidencia exacta de string. Sin prefijo, sin Bearer, sin encoding.

Si te provisionaron por IP, no hace falta ningún header — leemos tu IP de origen desde X-Forwarded-For, después X-Real-IP, y por último el socket peer.

  • La coincidencia es por string exacto contra la IP que registramos.
  • No soportamos rangos CIDR / subnets. Cada IP de salida individual que uses tiene que estar registrada. Si tu infraestructura rota IPs, usá el método de API key en su lugar.

La regla del “el lead tiene que existir”

Sección titulada «La regla del “el lead tiene que existir”»

La autenticación no depende solo de la credencial — también está atada a un lead real. Una solicitud es exitosa solo cuando se cumplen todas estas condiciones:

  1. Tu credencial (key o IP) coincide con un origen de lender registrado, y
  2. el click_id de la solicitud resuelve a un lead, y
  3. ese lead pertenece a un producto que es de tu cuenta de lender.

Si tu credencial es válida pero el click_id no resuelve a uno de tus leads, igual vas a recibir 403 (no 404). En la práctica esto significa: un click_id incorrecto, desconocido o ajeno devuelve 403.

click_id es el identificador único que conecta el journey del consumidor entre Prosperas y tus sistemas.

  • Prosperas lo genera y te lo entrega en el momento en que redirige al consumidor hacia vos (como identificador de tracking/click en la URL de handoff).
  • Tenés que guardarlo y devolverlo exactamente igual en cada postback de esa solicitud.
  • No contiene datos personales; es una referencia opaca.

Formatos aceptados:

Formato Ejemplo Notas
UUID simple 7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e
orgname_UUID claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e Exactamente un guion bajo

Más de un guion bajo, o un UUID que no se pueda parsear, devuelve 400.

Método: POST con body JSON (recomendado para integraciones server-to-server). Si tu plataforma no puede emitir un POST con body, existe una variante GET totalmente soportada que recibe los mismos campos como query parameters — mismo endpoint, misma autenticación, mismo procesamiento.

Headers:

Content-Type: application/json
X-API-KEY: <tu-key>

Body (JSON, snake_case):

{
"click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "approval",
"occurred_at": "2026-07-01T12:34:56Z",
"data": {
"provider": "tu-marca",
"client": {
"email": "consumidor@example.com",
"fullName": "Ana Gomez",
"firstName": "Ana",
"lastName": "Gomez",
"idNumber": "1234567890",
"phoneNumber": "3001234567",
"returningCustomer": false
}
}
}
Campo Tipo Requerido Notas
click_id string Sí El identificador de tracking. Devolvelo exactamente igual.
event string Sí Uno de los 9 eventos soportados: application, approval, disbursement, rejected, no_payment_yet, on_time, late, default, paid_in_full (ver Eventos y ciclo de vida más abajo). Solo minúsculas — en mayúsculas se rechaza con 422.
occurred_at string — Timestamp ISO-8601 de cuándo ocurrió el evento de tu lado (ej. 2026-07-01T12:34:56Z).
data object — Blob de auditoría opcional, de forma libre. Claves reconocidas: provider (string) y client (object, ver abajo). Para los eventos de estado de pago existen además claves recomendadas (ver Actualizaciones de estado de pago más abajo); no son obligatorias ni se validan.
data.client object — Datos opcionales del consumidor. Ver sección siguiente.

data.client te permite enriquecer el registro del consumidor. Todos los campos son opcionales.

Campo Tipo Notas
email string
firstName string Se usa solo si fullName no está presente.
lastName string Se usa solo si fullName no está presente.
fullName string Tiene prioridad: primer token → nombre, resto → apellido.
idNumber string Número de documento / cédula.
phoneNumber string
returningCustomer boolean Por defecto es false si se omite. Ver la advertencia abajo.

Si tu plataforma no puede emitir un POST con body JSON — un tag manager, un sistema legacy, un motor de reglas que solo sabe invocar URLs — podés reportar exactamente el mismo evento con un GET sobre el mismo endpoint, pasando los campos como query parameters:

GET {BASE_URL}/client_app/webhook/lender/loan-status?click_id=<id>&event=<evento>

Atraviesa la misma autenticación, el mismo mapeo de evento → estado y la misma persistencia que el POST. Devuelve el mismo acknowledgement JSON (200) y los mismos códigos de error (400 / 403 / 404 / 422 / 5xx), con las mismas reglas de reintento.

Parámetro Tipo Requerido Equivale en el POST a
click_id string Sí click_id
event string Sí event (minúsculas; uno de los 9 eventos)
occurred_at string — occurred_at (ISO-8601)
api_key string — Header X-API-KEY (ver abajo)
provider string — data.provider
email string — data.client.email
firstName string — data.client.firstName
lastName string — data.client.lastName
fullName string — data.client.fullName
idNumber string — data.client.idNumber
phoneNumber string — data.client.phoneNumber
returningCustomer boolean — data.client.returningCustomer

Todos los valores tienen que ir URL-encoded (emails, nombres con espacios, el + de un teléfono internacional). Los parámetros que no estén en esta tabla se ignoran y no se almacenan.

Las tres formas de autenticarte siguen valiendo, en este orden de preferencia:

  1. Header X-API-KEY — recomendado también en el GET. Un GET puede llevar headers; si tu cliente puede setearlos, usá esto.
  2. Query parameter api_key — alternativa para clientes que no pueden setear headers.
  3. IP allowlist — sin credencial en la request, si tu IP de origen está registrada.
Ventana de terminal
curl -G "{BASE_URL}/client_app/webhook/lender/loan-status" \
-H "X-API-KEY: <tu-key>" \
--data-urlencode "click_id=claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e" \
--data-urlencode "event=approval" \
--data-urlencode "occurred_at=2026-07-01T12:34:56Z" \
--data-urlencode "provider=tu-marca" \
--data-urlencode "email=consumidor@example.com" \
--data-urlencode "fullName=Ana Gomez"
{ "ok": true, "lead_id": 12345, "user_id": 678, "status": "APPROVAL" }

Enviá un postback por cada transición de estado, usando el evento que corresponda a lo que pasó:

event Significado ¿Requerido?
application Se recibió / inició la solicitud del consumidor de tu lado. Opcional
approval La solicitud fue aprobada. Sí
disbursement Se desembolsaron los fondos. Sí
rejected La solicitud fue rechazada. Sí
no_payment_yet El préstamo fue desembolsado pero todavía no llegó la primera fecha de pago programada. Sí — requerido
on_time Se recibió el pago en la fecha de vencimiento (o dentro del período de gracia estándar). Sí — requerido
late Se recibió el pago después del vencimiento + gracia, por debajo del umbral de default de tu institución. Sí — requerido
default El préstamo cumplió el criterio interno de default de tu institución. Sí — requerido
paid_in_full Se saldaron todos los pagos programados y el saldo pendiente; el préstamo queda cerrado en buen estado. Sí — requerido

application se acepta pero es opcional — enviálo si tenés un hito significativo de “solicitud recibida”; si no, los tres resultados terminales (approval, disbursement, rejected) son lo que más importa.

Los cinco eventos de estado de pago (no_payment_yet, on_time, late, default, paid_in_full) son distintos de los cuatro anteriores: no son un hito único sino actualizaciones continuas durante toda la vida del préstamo, reportadas después del desembolso. Se van reemplazando entre sí a medida que cambia la situación de pago — no_payment_yet es el estado inicial y se reemplaza por on_time, late o default en cuanto pasa la primera fecha de vencimiento. Reportar los cinco eventos es obligatorio para todos los leads, con vigencia antes del 31 de agosto. Los datos de aprobación y desembolso solo dicen quién calificó para el crédito; los datos de pago cierran el círculo sobre quién efectivamente repaga, lo que mejora la calidad del matchmaking para todos los lenders. Ver la sección Actualizaciones de estado de pago más abajo para el detalle de cada estado.

Enviá los eventos en el orden en que ocurren. Prosperas tolera eventos que llegan desordenados, pero la entrega en orden mantiene limpio el historial del lead.

A diferencia de los cuatro eventos anteriores, que marcan un hito único en la solicitud, las actualizaciones de estado de pago son un reporte continuo: enviás un nuevo postback cada vez que cambia la situación de pago del consumidor, durante toda la vida del préstamo.

event Definición
no_payment_yet El préstamo fue desembolsado pero todavía no llegó la primera fecha de pago programada. Es el estado inicial y saludable — se reemplaza por on_time, late o default en cuanto pasa esa fecha.
on_time Se recibió el pago en la fecha de vencimiento, o dentro del período de gracia estándar de tu institución.
late Se recibió el pago después del vencimiento + gracia, pero todavía por debajo del umbral de default de tu institución.
default El préstamo cumplió tu criterio interno de default.
paid_in_full Se saldaron todos los pagos programados y el saldo pendiente (capital, intereses, cargos). El préstamo queda cerrado en buen estado.

Para late, se recomienda reportar el bucket de días de mora (DPD, days past due) en data.dpd_bucket:

Bucket Rango
1-30 1 a 30 días de mora
31-60 31 a 60 días de mora
61-90 61 a 90 días de mora

El criterio más común de la industria para default es 90 o más días de mora. Si tu institución usa un umbral distinto, reportalo explícitamente en data.default_threshold_dpd para que quede claro qué convención estás aplicando.

Evento Claves recomendadas en data
no_payment_yet disbursed_amount
on_time —
late dpd_bucket
default default_threshold_dpd
paid_in_full paid_off_date

no_payment_yet

{
"click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "no_payment_yet",
"occurred_at": "2026-08-01T09:00:00Z",
"data": { "disbursed_amount": 500000 }
}

on_time

{
"click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "on_time",
"occurred_at": "2026-09-01T09:00:00Z",
"data": {}
}

late

{
"click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "late",
"occurred_at": "2026-09-15T09:00:00Z",
"data": { "dpd_bucket": "1-30" }
}

default

{
"click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "default",
"occurred_at": "2026-12-01T09:00:00Z",
"data": { "default_threshold_dpd": 90 }
}

paid_in_full

{
"click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "paid_in_full",
"occurred_at": "2027-06-01T09:00:00Z",
"data": { "paid_off_date": "2027-06-01" }
}

Body de éxito (200 OK):

{
"ok": true,
"lead_id": 12345,
"status": "APPROVAL",
"user_id": 6789
}
  • ok — siempre true en caso de éxito.
  • lead_id — el lead de Prosperas contra el cual se registró el evento.
  • status — el estado interno al que mapeó tu evento (APPLICATION / APPROVAL / DISBURSEMENT / REJECTED / NO_PAYMENT_YET / ON_TIME / LATE / DEFAULT / PAID_IN_FULL).
  • user_id — el id del registro del consumidor, cuando está disponible.

Códigos de estado:

Código Significado Qué hacer
200 Evento registrado. Listo.
400 click_id mal formado (UUID inválido o demasiados guiones bajos). Corregí el click_id; no reintentes sin cambios.
403 Falló la autenticación o el click_id no resuelve a uno de tus leads. Revisá tu key/IP y que estés devolviendo el click_id correcto. No reintentes a ciegas.
404 Sesión no encontrada (caso raro, ocurre después de que la autenticación fue exitosa). Verificá el click_id; contactá a Prosperas si persiste.
422 Error de validación — ej. clickId en camelCase, evento en mayúsculas, tipos incorrectos. Corregí la forma del payload (ver sección de errores comunes). No reintentes sin cambios.
5xx Error transitorio del lado de Prosperas. Reintentá con backoff exponencial (ver sección de reintentos).

Errores comunes (leé esto antes de integrar)

Sección titulada «Errores comunes (leé esto antes de integrar)»

Estos cuatro errores explican casi todas las integraciones fallidas en su primer intento. Evitalos:

❌ No hagas esto ✅ Hacé esto
Usar clickId en camelCase en el body Usá snake_case click_id — camelCase → 422
Enviar event: "APPROVAL" (mayúsculas) Enviá minúsculas approval — mayúsculas → 422
Asumir que el endpoint es público / sin auth Enviá X-API-KEY (o llamá desde una IP en la allowlist) — si no, 403
Poner la API key en un query string por comodidad Enviá la key en el header X-API-KEY — también se puede en un GET. ?api_key= existe solo como último recurso (ver variante GET)
  • Los reintentos son tu responsabilidad. Prosperas no te reintenta. Ante un 5xx o un error de red, reintentá el postback con backoff exponencial (ej. 1s, 2s, 4s, 8s…), hasta un límite razonable.
  • No reintentes ante 4xx. Un 400, 403 o 422 significa que la solicitud está mal — corregila y enviá una solicitud corregida en lugar de insistir con el mismo payload.
  • El endpoint no es idempotente. Cada postback aceptado registra un evento nuevo. Si reintentás después de una solicitud que en realidad tuvo éxito (pero cuya respuesta no recibiste), podés generar un evento duplicado. Mantené los reintentos acotados y priorizá reintentar solo ante fallas confirmadas.
  • Cada solicitud queda registrada de nuestro lado (con tu API key enmascarada) para soporte y conciliación.
Ventana de terminal
curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \
-H "Content-Type: application/json" \
-H "X-API-KEY: <tu-key>" \
-d '{
"click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "approval",
"occurred_at": "2026-07-01T12:34:56Z",
"data": { "provider": "tu-marca" }
}'

Respuesta — 200 OK:

{ "ok": true, "lead_id": 12345, "status": "APPROVAL", "user_id": 6789 }

Desembolso con enriquecimiento de datos del consumidor (POST)

Sección titulada «Desembolso con enriquecimiento de datos del consumidor (POST)»
Ventana de terminal
curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \
-H "Content-Type: application/json" \
-H "X-API-KEY: <tu-key>" \
-d '{
"click_id": "7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e",
"event": "disbursement",
"occurred_at": "2026-07-01T15:00:00Z",
"data": {
"provider": "tu-marca",
"client": { "fullName": "Ana Gomez", "idNumber": "1234567890" }
}
}'
Ventana de terminal
# INCORRECTO: "clickId" (camelCase) en lugar de "click_id"
curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \
-H "Content-Type: application/json" \
-H "X-API-KEY: <tu-key>" \
-d '{ "clickId": "7e1c2f1f-...", "event": "approval" }'
# → 422 Unprocessable Entity (el campo "click_id" es requerido)

Error — credencial faltante o incorrecta (403)

Sección titulada «Error — credencial faltante o incorrecta (403)»
Ventana de terminal
# Sin header X-API-KEY y con una IP que no está en la allowlist → 403
curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \
-H "Content-Type: application/json" \
-d '{ "click_id": "7e1c2f1f-...", "event": "approval" }'
# → 403 Forbidden
Endpoint POST {BASE_URL}/client_app/webhook/lender/loan-status
Variante sin body GET al mismo path con los campos como query parameters — ver Variante GET
Header de auth X-API-KEY: <tu-key> (no distingue mayúsculas/minúsculas)
Campos requeridos del body click_id (snake_case), event (minúsculas)
Eventos application · approval · disbursement · rejected · no_payment_yet · on_time · late · default · paid_in_full
Campos opcionales del body occurred_at (ISO-8601), data, data.client
Éxito 200 → { ok, lead_id, status, user_id }
Errores del cliente 400 click_id inválido · 403 auth/lead · 404 sesión · 422 validación
Reintentar ante Solo 5xx / red, con backoff exponencial
Firma Ninguna (sin HMAC) — protegé la API key

Siguiente paso: con los postbacks funcionando, cerrá la integración con los Datos de producto.


Última verificación: 2026-09-15 · v1.3 · coincide con api/client_app/webhook/router.py