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_changed and, 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:

StatusMeaning
pendingPayment not yet confirmed.
in_progressPayment processing (e.g. on-chain).
completedPayment successfully completed and not reversed.
failedPayment failed.
reversingAt least one reversal exists and is not in on_hold or error; reversal is in progress.
reversedAt 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:

StatusMeaning
not_paidNo payment applied.
partially_paidSome payment applied; not fully paid.
paidFully paid (within the current calculation).
loan_not_disbursedLoan not yet disbursed.
endorsedEndorsed (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

This event is sent on every reversal creation and on every reversal status change. Use it to track the reversal lifecycle.

Payload fields:

FieldTypeDescription
uuidstringReversal UUID (use for idempotency).
statusstringCurrent reversal status (see table below).
reversed_atstring | nullISO datetime of the reversal at the bank; may be null.
facility_repayobjectRelated facility repay.
facility_repay.uuidstringFacility repay UUID.
facility_repay.statusstringFacility repay status (e.g. reversing, reversed).

Reversal status values:

StatusMeaning
on_holdDetected but not yet processing.
pendingWaiting to start pre-steps.
pre_steps_startedPre-steps (e.g. fee refunds) in progress.
pre_steps_finishedPre-steps completed.
approve_tx_startedProvider fee approval transaction(s) submitted.
approve_tx_finishedApproval transaction(s) confirmed.
tx_startedreverseRepay transaction submitted.
tx_finishedreverseRepay transaction confirmed.
errorReversal 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

When 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:

FieldTypeDescription
installment_uuidstringInstallment UUID.
repayment_scheduleobjectSchedule info.
repayment_schedule.repayment_schedule_uuidstringSchedule UUID.
repayment_schedule.is_activebooleanWhether the schedule is active.
loanobjectLoan info.
loan.loan_uuidstringLoan UUID.
fixed_amountstringFixed installment amount (decimal string).
due_datestring | nullDue datetime ISO format; may be null.
installment_numberintegerInstallment number.
statusstringDerived status (e.g. not_paid, partially_paid, paid).
last_payment_datestring | nullLast payment date ISO; may be null.
present_valuestringPresent value (decimal string).
total_paid_and_settledstringTotal paid and settled (decimal string).
total_paid_in_progressstringTotal paid in progress (decimal string).
total_paidstringTotal paid (decimal string).
total_discountstringTotal discount (decimal string).
total_settledstringTotal settled (decimal string).
is_fully_paidbooleanWhether the installment is fully paid.
outstanding_amountstringOutstanding amount (decimal string).
pending_amountstringPending 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 optionally created_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 in reversing for a long time or you receive an error status 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_changed and installment_status_changed in your provider/webhook settings.