Facility Repay Reversal – Integration Guide
This guide explains how facility repay reversals work and how to integrate with them via webhooks and the API. It is intended for backend developers building against the Kona Finance API.
Overview
When a payment is reversed by the bank (e.g. Stark Bank, due to tax ID restrictions, payer account rules, or central bank rules), Kona Finance detects the reversal, runs internal processing (fee refunds, on-chain reverseRepay), and notifies your integration via webhooks. You can also use the repayments API to see when a repayment is in reversing or reversed status.
Prerequisites
- Your provider is configured in Kona Finance.
- Your webhook URL is subscribed to the events
facility_repay_reversal_status_changedand, if you need updated installment data,installment_status_changed.
Status behavior before and after reversal
Facility repay status
Each facility repayment has a status derived from its transaction and any reversals. Possible values (from the facility repay resource) are:
| Status | Meaning |
|---|---|
pending | Payment not yet confirmed. |
in_progress | Payment processing (e.g. on-chain). |
completed | Payment successfully completed and not reversed. |
failed | Payment failed. |
reversing | At least one reversal exists and is not in on_hold or error; reversal is in progress. |
reversed | At least one reversal has reached tx_finished; the repayment is fully reversed. |
Before a reversal: The repayment is typically completed. After a reversal is completed: The repayment status becomes reversed. While the reversal is being processed (after it leaves on_hold and until it reaches tx_finished or error), the repayment status is reversing.
Installment status
Installment amounts (e.g. total_paid_and_settled, present value, outstanding amount) exclude allocations that belong to a facility repay which has a reversal in status tx_finished. So once a reversal is completed (tx_finished), the affected installments are effectively reverted: amounts that were counted as paid are no longer counted, and the derived installment status can go back from paid to partially_paid or not_paid, depending on other payments.
Possible installment status values (derived from these calculations) are:
| Status | Meaning |
|---|---|
not_paid | No payment applied. |
partially_paid | Some payment applied; not fully paid. |
paid | Fully paid (within the current calculation). |
loan_not_disbursed | Loan not yet disbursed. |
endorsed | Endorsed (context-specific). |
After a reversal is completed (tx_finished), you will receive installment_status_changed webhooks for each affected installment with the updated amounts and status.
Webhooks
All webhooks are sent with the same top-level envelope:
created_at– ISO 8601 datetime when the event was created.event– Event type string.payload– Event-specific JSON object.
Your endpoint receives a POST with a JSON body like:
{
"created_at": "YYYY-MM-DDTHH:MM:SSZ",
"event": "<event_type>",
"payload": { ... }
}Event: facility_repay_reversal_status_changed
facility_repay_reversal_status_changedThis event is sent on every reversal creation and on every reversal status change. Use it to track the reversal lifecycle.
Payload fields:
| Field | Type | Description |
|---|---|---|
uuid | string | Reversal UUID (use for idempotency). |
status | string | Current reversal status (see table below). |
reversed_at | string | null | ISO datetime of the reversal at the bank; may be null. |
facility_repay | object | Related facility repay. |
facility_repay.uuid | string | Facility repay UUID. |
facility_repay.status | string | Facility repay status (e.g. reversing, reversed). |
Reversal status values:
| Status | Meaning |
|---|---|
on_hold | Detected but not yet processing. |
pending | Waiting to start pre-steps. |
pre_steps_started | Pre-steps (e.g. fee refunds) in progress. |
pre_steps_finished | Pre-steps completed. |
approve_tx_started | Provider fee approval transaction(s) submitted. |
approve_tx_finished | Approval transaction(s) confirmed. |
tx_started | reverseRepay transaction submitted. |
tx_finished | reverseRepay transaction confirmed. |
error | Reversal failed. |
Example: Reversal created (on_hold)
{
"created_at": "2025-02-20T14:30:00Z",
"event": "facility_repay_reversal_status_changed",
"payload": {
"uuid": "<reversal_uuid>",
"status": "on_hold",
"reversed_at": "2025-02-20T14:25:00Z",
"facility_repay": {
"uuid": "<facility_repay_uuid>",
"status": "completed"
}
}
}Example: Reversal completed (tx_finished)
{
"created_at": "2025-02-20T16:00:00Z",
"event": "facility_repay_reversal_status_changed",
"payload": {
"uuid": "<reversal_uuid>",
"status": "tx_finished",
"reversed_at": "2025-02-20T14:25:00Z",
"facility_repay": {
"uuid": "<facility_repay_uuid>",
"status": "reversed"
}
}
}Event: installment_status_changed
installment_status_changedWhen a reversal reaches status tx_finished, one installment_status_changed webhook is sent per affected installment (i.e. per allocation of that facility repay). Use it to update your view of installment amounts and status after a reversal.
Payload fields:
| Field | Type | Description |
|---|---|---|
installment_uuid | string | Installment UUID. |
repayment_schedule | object | Schedule info. |
repayment_schedule.repayment_schedule_uuid | string | Schedule UUID. |
repayment_schedule.is_active | boolean | Whether the schedule is active. |
loan | object | Loan info. |
loan.loan_uuid | string | Loan UUID. |
fixed_amount | string | Fixed installment amount (decimal string). |
due_date | string | null | Due datetime ISO format; may be null. |
installment_number | integer | Installment number. |
status | string | Derived status (e.g. not_paid, partially_paid, paid). |
last_payment_date | string | null | Last payment date ISO; may be null. |
present_value | string | Present value (decimal string). |
total_paid_and_settled | string | Total paid and settled (decimal string). |
total_paid_in_progress | string | Total paid in progress (decimal string). |
total_paid | string | Total paid (decimal string). |
total_discount | string | Total discount (decimal string). |
total_settled | string | Total settled (decimal string). |
is_fully_paid | boolean | Whether the installment is fully paid. |
outstanding_amount | string | Outstanding amount (decimal string). |
pending_amount | string | Pending amount (decimal string). |
Example: Installment after reversal completed
{
"created_at": "2025-02-20T16:00:01Z",
"event": "installment_status_changed",
"payload": {
"installment_uuid": "<installment_uuid>",
"repayment_schedule": {
"repayment_schedule_uuid": "<schedule_uuid>",
"is_active": true
},
"loan": {
"loan_uuid": "<loan_uuid>"
},
"fixed_amount": "1500.00",
"due_date": "2025-02-15T00:00:00Z",
"installment_number": 3,
"status": "partially_paid",
"last_payment_date": "2025-02-10T12:00:00Z",
"present_value": "1520.50",
"total_paid_and_settled": "800.00",
"total_paid_in_progress": "0.00",
"total_paid": "800.00",
"total_discount": "0.00",
"total_settled": "800.00",
"is_fully_paid": false,
"outstanding_amount": "720.50",
"pending_amount": "720.50"
}
}Querying reversals via the API
There is no dedicated API endpoint for listing or retrieving facility repay reversal records. Use the following instead.
Webhooks (primary)
Webhooks are the primary way to learn about reversals and their status changes. Subscribe to facility_repay_reversal_status_changed to receive every reversal creation and status update. Use the reversal uuid in the payload for idempotency and correlation.
Facility repayments API
The repayments API exposes each repayment’s status, which reflects reversal state:
- List repayments:
GET /api/facilities/{facility_uuid}/repayments/ - Retrieve one repayment:
GET /api/facilities/{facility_uuid}/repayments/{repay_uuid}
When a repayment is being reversed, its status is reversing. When the reversal has completed (tx_finished), status is reversed. You can list repayments and filter client-side by status to find reversed or in-progress reversals. The response does not include a separate reversals array; the status field is the source of truth for reversal state at the repayment level.
A dedicated reversals list/retrieve endpoint may be added in the future. For now, use webhooks plus the facility repay status for integration.
Reversal lifecycle (high-level)
flowchart TD
subgraph detection [Detection]
A[Stark Bank reverses payment] --> B[Reversal detected]
B --> C[FacilityRepayReversal created]
C --> D[Status: on_hold]
end
subgraph processing [Processing]
D --> E[Status: pending]
E --> F[pre_steps_started / pre_steps_finished]
F --> G[approve_tx_started / approve_tx_finished]
G --> H[tx_started / tx_finished]
end
D --> W1[Webhook: facility_repay_reversal_status_changed]
E --> W1
F --> W1
G --> W1
H --> W1
H --> W2[Webhook: installment_status_changed per installment]
W1 --> End[Your system]
W2 --> End
Every time the reversal status changes, a facility_repay_reversal_status_changed webhook is sent. When the status becomes tx_finished, one installment_status_changed webhook is sent for each affected installment.
Troubleshooting
- Duplicate webhooks: Use the reversal
uuid(and optionallycreated_at+event) to deduplicate. Process each event idempotently. - Webhook delivery: The system retries failed webhook deliveries (e.g. non-2xx response) according to the configured policy. Ensure your endpoint returns 2xx on success and is idempotent.
- Repayment still
reversing: Reversal processing runs asynchronously. If a repayment stays inreversingfor a long time or you receive anerrorstatus on the reversal, contact support; the reversal may need manual handling.
Related documentation
- Repayment flow data structure – Entities and relationships for repayments and installments.
- Configure your webhook URL and subscribe to
facility_repay_reversal_status_changedandinstallment_status_changedin your provider/webhook settings.
Updated about 4 hours ago