Status postbacks
For: Lenders (integration team)
Overview
Section titled “Overview”When a consumer applies for one of your products through Prosperas, you process that application on your side (approve it, disburse it, reject it). A postback is how you tell Prosperas what happened, in near real time, so we can update the lead status, reconcile revenue, and keep the consumer experience in sync.
The integration is deliberately small: one endpoint, one credential, one payload shape.
| Property | Value |
|---|---|
| Direction | You → Prosperas (you call us) |
| Endpoint | POST {BASE_URL}/client_app/webhook/lender/loan-status |
| Auth | X-API-KEY header (recommended) or source-IP allowlist |
| Body format | JSON, snake_case |
| Events | application, approval, disbursement, rejected |
| Success | 200 OK with a JSON acknowledgement |
| Retries | You retry on 5xx / network errors (we do not retry you) |
Authentication
Section titled “Authentication”Every postback must authenticate. Prosperas checks the API key first, then the source IP. If neither matches a registered credential, the request is rejected with 403.
API key (recommended)
Section titled “API key (recommended)”Send your key in the HTTP header:
X-API-KEY: <your-key>- The header name is case-insensitive (
X-API-KEY,x-api-key,X-Api-Keyall work). - The key is matched by exact string. No prefix, no
Bearer, no encoding.
IP allowlist (alternative)
Section titled “IP allowlist (alternative)”If you were provisioned by IP, no header is required — we read your source IP from X-Forwarded-For, then X-Real-IP, then the socket peer.
- The match is exact string against the IP we registered.
- No CIDR / subnet ranges are supported. Every individual egress IP you use must be registered. If your infrastructure rotates IPs, use the API key method instead.
The “a lead must exist” rule
Section titled “The “a lead must exist” rule”Authentication is not only about the credential — it is bound to a real lead. A request succeeds only when all of the following hold:
- Your credential (key or IP) matches a registered lender origin, and
- the
click_idin the request resolves to a lead, and - that lead belongs to a product owned by your lender account.
If your credential is valid but the click_id does not resolve to one of your leads, you still get 403 (not 404). In practice this means: a wrong, unknown, or foreign click_id returns 403.
The tracking id (click_id)
Section titled “The tracking id (click_id)”click_id is the single identifier that ties a consumer’s journey together across Prosperas and your systems.
- Prosperas generates it and hands it to you at the moment the consumer is redirected to you (as the tracking / click identifier in the hand-off URL).
- You must store it and echo it back — exactly — in every postback for that application.
- It carries no personal data; it is an opaque reference.
Accepted formats:
| Format | Example | Notes |
|---|---|---|
| Bare UUID | 7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e |
|
orgname_UUID |
claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e |
Exactly one underscore |
More than one underscore, or an unparseable UUID, returns 400.
Request contract
Section titled “Request contract”Method: POST (recommended). A GET variant exists with the same fields as query parameters, but POST with a JSON body is the supported server-to-server method.
Headers:
Content-Type: application/jsonX-API-KEY: <your-key>Body (JSON, snake_case):
{ "click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e", "event": "approval", "occurred_at": "2026-07-01T12:34:56Z", "data": { "provider": "your-brand", "client": { "email": "consumer@example.com", "fullName": "Ana Gomez", "firstName": "Ana", "lastName": "Gomez", "idNumber": "1234567890", "phoneNumber": "3001234567", "returningCustomer": false } }}Field reference
Section titled “Field reference”| Field | Type | Required | Notes |
|---|---|---|---|
click_id |
string | Yes | The tracking id described above. Echo it exactly. |
event |
string | Yes | One of application, approval, disbursement, rejected. Lowercase only — uppercase is rejected with 422. |
occurred_at |
string | — | ISO-8601 timestamp of when the event happened on your side (e.g. 2026-07-01T12:34:56Z). |
data |
object | — | Optional audit blob. Recognized keys: provider (string) and client (object below). |
data.client |
object | — | Optional consumer data. See next section. |
The data.client object (optional)
Section titled “The data.client object (optional)”data.client lets you enrich the consumer record. All fields are optional.
| Field | Type | Notes |
|---|---|---|
email |
string | |
firstName |
string | Used only if fullName is absent. |
lastName |
string | Used only if fullName is absent. |
fullName |
string | Takes priority: first token → first name, remainder → last name. |
idNumber |
string | National ID / document number. |
phoneNumber |
string | |
returningCustomer |
boolean | Defaults to false if omitted. See the caution below. |
Events & lifecycle
Section titled “Events & lifecycle”Send one postback per status transition, using the event that matches what happened:
event |
Meaning | Required? |
|---|---|---|
application |
The consumer’s application was received / started on your side. | Optional |
approval |
The application was approved. | Yes |
disbursement |
Funds were disbursed. | Yes |
rejected |
The application was rejected. | Yes |
application is accepted but optional — send it if you have a meaningful “application received” milestone; otherwise the three terminal outcomes (approval, disbursement, rejected) are what matter most.
Send events in the order they occur. Prosperas tolerates events arriving out of order, but in-order delivery keeps the lead history clean.
Responses & status codes
Section titled “Responses & status codes”Success body (200 OK):
{ "ok": true, "lead_id": 12345, "status": "APPROVAL", "user_id": 6789}ok— alwaystrueon success.lead_id— the Prosperas lead the event was recorded against.status— the internal status your event mapped to (APPLICATION/APPROVAL/DISBURSEMENT/REJECTED).user_id— the consumer record id, when available.
Status codes:
| Code | Meaning | What you should do |
|---|---|---|
200 |
Event recorded. | Done. |
400 |
Malformed click_id (bad UUID or too many underscores). |
Fix the click_id; do not retry unchanged. |
403 |
Auth failed or the click_id doesn’t resolve to one of your leads. |
Check your key/IP and that you’re echoing the correct click_id. Do not blindly retry. |
404 |
Session not found (rare edge, after auth succeeds). | Verify the click_id; contact Prosperas if it persists. |
422 |
Validation error — e.g. camelCase clickId, uppercase event, wrong types. |
Fix the payload shape (see common mistakes below). Do not retry unchanged. |
5xx |
Transient Prosperas-side error. | Retry with exponential backoff (see below). |
Common mistakes (read this before you build)
Section titled “Common mistakes (read this before you build)”These four mistakes account for almost every failed first integration. Avoid them:
| ❌ Don’t | ✅ Do |
|---|---|
Use camelCase clickId in the body |
Use snake_case click_id — camelCase → 422 |
Send event: "APPROVAL" (uppercase) |
Send lowercase approval — uppercase → 422 |
| Assume the endpoint is public / no auth | Send X-API-KEY (or call from an allowlisted IP) — else 403 |
| Put the API key in a query string or expect the pixel to authenticate | Send the key in the X-API-KEY header on a POST |
Retries & delivery guarantees
Section titled “Retries & delivery guarantees”- You own retries. Prosperas does not retry you. On a
5xxor a network failure, retry the postback with exponential backoff (e.g. 1s, 2s, 4s, 8s…), up to a sensible ceiling. - Do not retry on
4xx. A400,403, or422means the request is wrong — fix it and send a corrected request instead of hammering the same payload. - The endpoint is not idempotent. Each accepted postback records a new event. If you retry after a request that actually succeeded (but whose response you missed), you may create a duplicate event. Keep retries bounded and prefer retrying only on confirmed failures.
- Every request is logged on our side (with your API key masked) for support and reconciliation.
Worked examples
Section titled “Worked examples”Happy path — approval (POST)
Section titled “Happy path — approval (POST)”curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \ -H "Content-Type: application/json" \ -H "X-API-KEY: <your-key>" \ -d '{ "click_id": "claro_7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e", "event": "approval", "occurred_at": "2026-07-01T12:34:56Z", "data": { "provider": "your-brand" } }'Response — 200 OK:
{ "ok": true, "lead_id": 12345, "status": "APPROVAL", "user_id": 6789 }Disbursement with consumer enrichment (POST)
Section titled “Disbursement with consumer enrichment (POST)”curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \ -H "Content-Type: application/json" \ -H "X-API-KEY: <your-key>" \ -d '{ "click_id": "7e1c2f1f-9b4d-4c1d-9f6c-2f3c1a2b4d5e", "event": "disbursement", "occurred_at": "2026-07-01T15:00:00Z", "data": { "provider": "your-brand", "client": { "fullName": "Ana Gomez", "idNumber": "1234567890" } } }'Error — camelCase field (422)
Section titled “Error — camelCase field (422)”# WRONG: "clickId" (camelCase) instead of "click_id"curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \ -H "Content-Type: application/json" \ -H "X-API-KEY: <your-key>" \ -d '{ "clickId": "7e1c2f1f-...", "event": "approval" }'# → 422 Unprocessable Entity (field "click_id" is required)Error — missing / wrong credential (403)
Section titled “Error — missing / wrong credential (403)”# No X-API-KEY header and IP not allowlisted → 403curl -X POST "{BASE_URL}/client_app/webhook/lender/loan-status" \ -H "Content-Type: application/json" \ -d '{ "click_id": "7e1c2f1f-...", "event": "approval" }'# → 403 ForbiddenQuick reference
Section titled “Quick reference”| Endpoint | POST {BASE_URL}/client_app/webhook/lender/loan-status |
| Auth header | X-API-KEY: <your-key> (case-insensitive) |
| Required body fields | click_id (snake_case), event (lowercase) |
| Events | application · approval · disbursement · rejected |
| Optional body fields | occurred_at (ISO-8601), data, data.client |
| Success | 200 → { ok, lead_id, status, user_id } |
| Client errors | 400 bad click_id · 403 auth/lead · 404 session · 422 validation |
| Retry on | 5xx / network only, exponential backoff |
| Signing | None (no HMAC) — protect the API key |
Important notes
Section titled “Important notes”Next step: with postbacks working, close out the integration with Product data.
Last verified: 2026-07-01 · v1.1 · matches Lender-Postback-Integration-Guide.md