kamiPay LogokamiPay Docs

Settlement

Receive a notification with the full charge list when a settlement is finalized

When a batched settlement is finalized, kamiPay sends a settlement.settled webhook to your configured endpoint. The payload contains the aggregate amounts for the settlement and the full list of charges that were included in it, so you can reconcile each individual charge against your own system without an extra API round-trip. One webhook delivery per scheduled batch summarises the disbursement and lists every charge that was part of it.

This webhook is specific to the batched settlement program — it fires for both Fiwind batched and blockchain batched settlements. If you are on the legacy 1:1 mode you do not receive settlement.settled — each charge is reconciled individually through the per-charge charge webhook the moment it completes.

Blockchain batched settlements fire settlement.settled once the bulk USDT transfer is mined on-chain (there is no provider webhook on that path). There is no separate per-charge settlement webhook — each charge is acknowledged immediately by its per-charge done charge webhook, and the single settlement.settled closes the batch.


settlement.settled

Sent once per settlement when the underlying transfer is confirmed — provider confirmation for Fiwind batched, or the bulk transfer being mined for blockchain batched — and the settlement transitions to DONE.

The payload shape is identical for both modes, but the currencies and amounts differ. The Fiwind batched payload settles in ARS (with the USDT amount kamiPay moved to the provider on the source_* fields). The blockchain batched payload settles in USDt and carries no ARS fields — there is no requote, and each charge's settlement_amount is in USDt.

Both payloads also carry a refunds[] array listing the pay-in refunds that were deducted from this settlement (documented in full further down). It is always present — an empty array when there were none. When it is non-empty, source_amount is lower than source_net_price by the total deducted:

Σ charges[].settlement_amount  −  Σ refunds[].deduction_amount  =  source_amount
{
  "event": "settlement.settled",
  "settlement_id": 12345,
  "provider_settlement_id": "psid_8f3c1d2a9e",
  "external_settlement_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f",
  "amount": 99325.0,
  "currency_id": 32,
  "source_amount": 68.5,
  "source_currency_id": 9999,
  "source_net_price": 68.3,
  "settled_at": "2026-05-13T15:00:42Z",
  "charges": [
    {
      "kamipay_id": "txc_01j3t9fxqffrva9s8d1ekm4g3v",
      "external_id": "merchant-order-aaa-11112",
      "kamipay_request_id": "ptxr_01j3t9fxqffrva9s8d1ekm4g3w",
      "charged_amount": 5.28,
      "charged_currency": "BRL",
      "settlement_amount": 29750.0,
      "settlement_currency": "ARS"
    },
    {
      "kamipay_id": "txc_01j4a2x6m1d8h6e5b3t7w1ku9k",
      "external_id": "merchant-order-aaa-11113",
      "kamipay_request_id": "ptxr_01j4a2x6m1d8h6e5b3t7w1ku9l",
      "charged_amount": 7.04,
      "charged_currency": "BRL",
      "settlement_amount": 39575.0,
      "settlement_currency": "ARS"
    },
    {
      "kamipay_id": "txc_01j4c8e7n3p2q5r9w1m6b8y4hj",
      "external_id": null,
      "kamipay_request_id": "ptxr_01j4c8e7n3p2q5r9w1m6b8y4hk",
      "charged_amount": 5.28,
      "charged_currency": "BRL",
      "settlement_amount": 29750.0,
      "settlement_currency": "ARS"
    }
  ],
  "refunds": []
}
{
  "event": "settlement.settled",
  "settlement_id": 67890,
  "provider_settlement_id": "0x9c1de80f5a672bc41e9d3fa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b",
  "external_settlement_id": "0x9c1de80f5a672bc41e9d3fa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b",
  "amount": 57.5,
  "currency_id": 9999,
  "source_amount": 57.5,
  "source_currency_id": 9999,
  "source_net_price": 60.0,
  "settled_at": "2026-05-13T15:00:42Z",
  "charges": [
    {
      "kamipay_id": "txc_01j3t9fxqffrva9s8d1ekm4g3v",
      "external_id": "merchant-order-aaa-11112",
      "kamipay_request_id": "ptxr_01j3t9fxqffrva9s8d1ekm4g3w",
      "charged_amount": 52.8,
      "charged_currency": "BRL",
      "settlement_amount": 10.0,
      "settlement_currency": "USDt"
    },
    {
      "kamipay_id": "txc_01j4a2x6m1d8h6e5b3t7w1ku9k",
      "external_id": "merchant-order-aaa-11113",
      "kamipay_request_id": "ptxr_01j4a2x6m1d8h6e5b3t7w1ku9l",
      "charged_amount": 105.6,
      "charged_currency": "BRL",
      "settlement_amount": 20.0,
      "settlement_currency": "USDt"
    },
    {
      "kamipay_id": "txc_01j4c8e7n3p2q5r9w1m6b8y4hj",
      "external_id": null,
      "kamipay_request_id": "ptxr_01j4c8e7n3p2q5r9w1m6b8y4hk",
      "charged_amount": 158.4,
      "charged_currency": "BRL",
      "settlement_amount": 30.0,
      "settlement_currency": "USDt"
    }
  ],
  "refunds": [
    {
      "kamipay_refund_id": "rpi_5b7c2d4e6a8f0b1c3d5e7f90",
      "kamipay_id": "txc_01j4a2x6m1d8h6e5b3t7w1ku9k",
      "external_id": "merchant-order-aaa-11113",
      "kamipay_request_id": "ptxr_01j4a2x6m1d8h6e5b3t7w1ku9l",
      "refunded_amount": 13.2,
      "refunded_currency": "BRL",
      "deduction_amount": 2.5,
      "deduction_currency": "USDt"
    }
  ]
}

The three charges add up to source_net_price (10 + 20 + 30 = 60), the refund deducts 2.5, and amount / source_amount are the resulting 57.5 — the USDt actually transferred to your wallet.

Telling the two apart: read currency_id / settlement_currency. The blockchain batched payload has no ARS anywhere — every amount is USDt — and the external_settlement_id is the on-chain hash of the single bulk transfer. With no refunds deducted, amount, source_amount, and source_net_price are one and the same USDt total, because there is no requote on this path.

Top-level fields

FieldTypeDescription
eventstringAlways "settlement.settled".
settlement_idintegerUnique identifier of the settlement (matches settlement_id returned by GET /v1/settlements).
provider_settlement_idstring | nullProvider-side identifier for the transfer.
external_settlement_idstring | nullAuxiliary external reference (e.g. originating vault transaction hash for off-chain settlements).
amountnumberAmount actually delivered to your destination address, in currency_id units.
currency_idintegerCurrency id of amount.
source_amountnumberAmount kamiPay actually transferred to the provider for this settlement, in source_currency_id units.
source_currency_idintegerCurrency id of source_amount.
source_net_pricenumberGross accumulated value of the included charges on the source side, in source_currency_id units — charges only, before any refund deduction. For Fiwind batched, the difference against source_amount also reflects internal repricing absorbed by the settlement. For blockchain batched there is no requote, so the only thing that can separate the two is a refund deduction: with an empty refunds[], source_net_price equals source_amount (and amount).
settled_atdatetimeISO 8601 timestamp when the settlement was marked DONE.
chargesarrayAll charges included in this settlement. See below.
refundsarrayPay-in refunds deducted from this settlement. Always present; an empty array when none were deducted. See below.

charges[] fields

The charges[] array contains one item per charge included in the settlement, ordered by the time each charge was added to the settlement (oldest first).

FieldTypeDescription
kamipay_idstringThe QR/transaction kamiPay identifier of the original charge (matches the value returned at QR creation and the kamipay_id on the corresponding charge webhook).
external_idstring | nullYour own identifier for the charge, as you supplied it at checkout time. null if you did not provide one. This is the primary reconciliation key for the merchant — the same value appears on the per-charge done event of the charge webhook.
kamipay_request_idstringPay-in request identifier (Pix id).
charged_amountnumberAmount charged to the payor, typically BRL.
charged_currencystringCurrency code (alpha3) of charged_amount, e.g. BRL.
settlement_amountnumber | nullThe amount the charge was quoted at — the settlement-side promise, expressed in the currency the checkout quotes in. null only for charges with no quote linked.
settlement_currencystring | nullCurrency code (alpha3) of settlement_amount — the quote currency of the checkout quote layout (e.g. ARS, USDt, BRL).

refunds[] fields

The refunds[] array contains one item per pay-in refund whose amount was subtracted from this settlement. A refund is deducted from the first settlement that runs after it is confirmed — which is usually not the settlement that contained the original charge, so the kamipay_id of a refund often refers to a charge you reconciled in an earlier batch.

FieldTypeDescription
kamipay_refund_idstringIdentifier of the refund itself (rpi_…).
kamipay_idstringThe kamiPay identifier of the original charge being refunded.
external_idstring | nullYour own identifier for that original charge, as supplied at checkout time. null if you did not provide one. This is the reconciliation key — it matches the external_id you already recorded for the charge.
kamipay_request_idstringPay-in request identifier (Pix id) of the original charge.
refunded_amountnumberAmount returned to the payor, typically BRL. Matches the value you requested (or the full charge amount when you omitted it).
refunded_currencystringCurrency code (alpha3) of refunded_amount, e.g. BRL.
deduction_amountnumberAmount subtracted from this settlement, in source_currency_id units. Prorated at the original charge's own rate, so a full refund cancels the charge exactly.
deduction_currencystringCurrency code (alpha3) of deduction_amount.

If the pending refund deductions for a checkout add up to more than the charges waiting to be settled, no settlement is produced that cycle — a zero or negative transfer makes no sense. Both the charges and the refunds stay pending and are netted in a later cycle, once enough new charges have accumulated to absorb the deduction.


Reconciliation

The intended flow for merchants:

  1. As charges complete, you receive a done event on the charge webhook for each one. Match it against your order using external_id and record it as collected.
  2. When the settlement is finalized you receive a single settlement.settled webhook. The charges[] array carries the same external_id for every charge you previously recorded.
  3. Use external_id to match each entry in charges[] back to your collected orders, and use amount (in currency_id units) to record the disbursement against your bank/wallet account.
  4. If refunds[] is non-empty, match each entry back by external_id too and record the deduction against that order. The originating charge was most likely settled in an earlier batch, so expect these to reference orders you have already reconciled.

Do not derive the disbursement by summing charges[] — that gives you the gross. amount is the net figure that hits your account, and the two differ by exactly Σ refunds[].deduction_amount.

The shape of charges[] exactly mirrors what GET /v1/settlements/{settlement_id} returns. If you ever miss a webhook delivery you can fetch the same data over REST and get the same reconciliation keys.


Signature validation, retries, and ordering

  • Signature: every webhook is signed with the same scheme as the charge and pay webhooks. Always validate the signature before processing the payload.
  • Idempotency: settlement_id is the natural idempotency key. kamiPay may retry the delivery (e.g. on a non-2xx response from your endpoint) and you may receive the same settlement.settled payload more than once. Treat repeated deliveries with the same settlement_id as no-ops after the first successful processing.
  • Ordering: settlement.settled is fired after all of the per-charge done events for the charges in charges[]. You may, however, receive a settlement.settled for one settlement before all done events for charges in a different settlement, so do not assume strict cross-settlement ordering.
  • Delivery channel: this event uses the same webhook endpoint and authentication configuration as your other kamiPay webhooks. No separate configuration is required.

Always validate the webhook signature using the method described in the Introduction before processing any webhook events.

On this page