kamiPay LogokamiPay Docs

Settlements

Reconcile incoming Pix charges against the off-chain transfers that delivered the funds to your wallet

Settlements track the transfers that move funds from kamiPay's vault to your destination address after one or more pay-in charges have been collected. Where Lots record the on-chain batches used for pay-outs, settlements record the off-chain disbursements that close a pay-in lifecycle.

In the legacy 1:1 mode, every successful charge produces its own settlement. In the batched settlement program (see below), multiple charges are grouped into a single settlement on a recurring schedule.


Batched Settlement Program

In the batched settlement program, charges are accumulated and grouped into a single settlement on a recurring schedule, replacing the per-charge disbursement flow with one consolidated transfer per cycle.

How it works

  1. As charges complete, each one is marked done and you receive the usual charge webhooks. The charge enters the "pending charges" pool for the next settlement cycle.
  2. On a recurring schedule, kamiPay groups all of your done charges that have no associated settlement into a single settlement.
  3. When the settlement is confirmed, you receive a settlement.settled webhook listing every charge it included (with your external_id per charge).

Behavior for non-enrolled merchants: all settlement endpoints below work for you regardless of mode. In 1:1 mode, the Pending Charges endpoint will always return an empty list because every charge is settled the moment it completes.

To enroll in the batched settlement program, contact your account manager.


List Settlements

GET /v1/settlements

List your settlements by settled_at date range. Use this to reconcile completed disbursements with charges and to audit the off-chain transfers that closed them.

Query Parameters

NameTypeDescription
start_datedatetimeRequired. Start of settled_at range. ISO 8601 with a UTC offset (e.g. 2026-05-01T00:00:00Z). A datetime without an offset is rejected with 400.
end_datedatetimeRequired. End of settled_at range. ISO 8601 with a UTC offset (e.g. 2026-05-31T23:59:59Z). A datetime without an offset is rejected with 400.
limitintMaximum results (1–1000). Default: 100.
offsetintPagination offset. Default: 0.

Maximum date range is 31 days. For longer periods, make multiple requests.

Example Request

const params = new URLSearchParams({
  start_date: '2026-05-01T00:00:00Z',
  end_date: '2026-05-28T23:59:59Z',
  limit: '100',
  offset: '0'
});

const url = `${baseURL}/v1/settlements?${params}`;

const response = await fetch(url, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${access_token}`,
    "Content-Type": "application/json",
  },
});
import requests

params = {
    'start_date': '2026-05-01T00:00:00Z',
    'end_date': '2026-05-28T23:59:59Z',
    'limit': 100,
    'offset': 0,
}

url = f"{base_url}/v1/settlements"

headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/json',
}

response = requests.get(url, params=params, headers=headers)
package main

import (
  "fmt"
  "net/http"
  "net/url"
)

func main() {
  baseURL := "https://api.kamipay.io"
  params := url.Values{}
  params.Add("start_date", "2026-05-01T00:00:00Z")
  params.Add("end_date", "2026-05-28T23:59:59Z")
  params.Add("limit", "100")
  params.Add("offset", "0")

  url := fmt.Sprintf("%s/v1/settlements?%s", baseURL, params.Encode())

  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", access_token))
  req.Header.Add("Content-Type", "application/json")

  client := &http.Client{}
  resp, err := client.Do(req)
  if err != nil {
    fmt.Println("Error:", err)
    return
  }
  defer resp.Body.Close()
}

Response

{
  "settlements": [
    {
      "settlement_id": 12345,
      "settlement_provider_name": "provider_x",
      "provider_settlement_id": "psid_8f3c1d2a9e",
      "external_settlement_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f",
      "amount": 1234567.89,
      "currency": "ARS",
      "address_to": "0x7a3F9b2C1e8D5462bA9c7F3e6D85907df41A2c3B",
      "address_from": "0x4f2D9aB16C3eD7849bA5d3E2F8C19075aE63C9B1",
      "settlement_message": null,
      "settled_at": "2026-05-14T15:00:42Z",
      "created_at": "2026-05-14T14:55:18Z",
      "status": "DONE"
    }
  ],
  "total": 1,
  "limit": 100,
  "offset": 0
}

Date range validation error

{
  "detail": "Date range cannot exceed 31 days"
}

Invalid date order

{
  "detail": "end_date must be after start_date"
}

Missing UTC offset

{
  "detail": "start_date must include a UTC offset (e.g. 2026-05-01T00:00:00Z)"
}
{
  "detail": "Incorrect Credentials"
}

Settlement Fields

FieldTypeDescription
settlement_idintegerUnique identifier of the settlement.
settlement_provider_namestringProvider that executed the transfer.
provider_settlement_idstring | nullProvider-side identifier for the transfer. null until the provider confirms.
external_settlement_idstring | nullAuxiliary external reference (e.g. on-chain transaction hash for blockchain settlements, originating vault tx hash for off-chain settlements).
amountnumberAmount delivered to your address_to, in currency units.
currencystringCurrency code (alpha3) of amount, e.g. ARS.
address_tostringDestination wallet/account where the funds were delivered.
address_fromstringOriginating kamiPay vault address.
settlement_messagestring | nullOptional message attached to the transfer.
settled_atdatetime | nullTimestamp the settlement was confirmed. null while still in flight.
created_atdatetimeSettlement-row creation time.
statusstring | nullCurrent status. See Settlement Status Values.

List Settlement Transactions

GET /v1/settlements/transactions

List the individual charges that have been included in your settlements, with the parent settlement's summary embedded on every row. Use this to drill from a settlement to its constituent charges, or to fetch a flat list of all charge-to-settlement associations in a window.

Query Parameters

NameTypeDescription
start_datedatetimeRequired. Start of the charge created_at range. ISO 8601 with a UTC offset (e.g. 2026-05-01T00:00:00Z). A datetime without an offset is rejected with 400.
end_datedatetimeRequired. End of the range. ISO 8601 with a UTC offset. A datetime without an offset is rejected with 400.
settlement_idintegerFilter to a specific settlement id.
limitintMaximum results (1–1000). Default: 100.
offsetintPagination offset. Default: 0.

Maximum date range is 31 days.

Example Request

const params = new URLSearchParams({
  start_date: '2026-05-01T00:00:00Z',
  end_date: '2026-05-28T23:59:59Z',
  settlement_id: '12345',
});

const url = `${baseURL}/v1/settlements/transactions?${params}`;

const response = await fetch(url, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${access_token}`,
    "Content-Type": "application/json",
  },
});
import requests

params = {
    'start_date': '2026-05-01T00:00:00Z',
    'end_date': '2026-05-28T23:59:59Z',
    'settlement_id': 12345,
}

url = f"{base_url}/v1/settlements/transactions"
headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/json',
}

response = requests.get(url, params=params, headers=headers)
params := url.Values{}
params.Add("start_date", "2026-05-01T00:00:00Z")
params.Add("end_date", "2026-05-28T23:59:59Z")
params.Add("settlement_id", "12345")

u := fmt.Sprintf("%s/v1/settlements/transactions?%s", baseURL, params.Encode())
req, _ := http.NewRequest("GET", u, nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", access_token))

Response

{
  "transactions": [
    {
      "settlement_id": 12345,
      "kamipay_request_id": "ptxr_01kr3m9p7n2s4d8h6e5b3t7w1k",
      "kamipay_id": "dqr_01kr3m9q5h7w2v4n6b8s3d5e9p",
      "external_id": "merchant-order-aaa-11112",
      "charged_amount": 5.28,
      "charged_currency": "BRL",
      "settlement_amount": 29750.0,
      "settlement_currency": "ARS",
      "charged_timestamp": "2026-05-14T13:21:08Z",
      "created_at": "2026-05-14T13:21:12Z",
      "settlement_provider_name": "provider_x",
      "settled_at": "2026-05-14T15:00:42Z",
      "provider_settlement_id": "psid_8f3c1d2a9e",
      "external_settlement_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f"
    }
  ],
  "total": 1,
  "limit": 100,
  "offset": 0
}

Date range validation error

{
  "detail": "Date range cannot exceed 31 days"
}

Missing UTC offset

{
  "detail": "end_date must include a UTC offset (e.g. 2026-05-01T00:00:00Z)"
}
{
  "detail": "Incorrect Credentials"
}

Transaction Fields

FieldTypeDescription
settlement_idintegerParent settlement id.
kamipay_request_idstringPay-in request identifier (Pix id).
kamipay_idstringThe QR/transaction kamiPay identifier (matches the value returned at QR creation).
external_idstring | nullYour own identifier for the charge, as supplied at checkout time. null if you did not provide one. This is the same value emitted on the per-charge done webhook. Use it as your primary reconciliation key.
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).
charged_timestampdatetime | nullWhen the pay-in request (QR/charge) was created.
created_atdatetimeTimestamp the charge was associated with the settlement.
settlement_provider_namestringProvider of the parent settlement.
settled_atdatetime | nullSettled-at timestamp of the parent settlement. null while in flight.
provider_settlement_idstring | nullProvider-side identifier of the parent settlement.
external_settlement_idstring | nullAuxiliary external reference of the parent settlement.

The charge core (charged_*, settlement_*, charged_timestamp) is the same shape the Pending Charges endpoint returns, so you can relate a settled transaction back to what you previously saw in the pending pool — the transactions endpoint just adds the parent-settlement fields on top.


Pending Charges

GET /v1/settlements/pending-charges

List the charges that are in done status but have not yet been associated with a settlement, for a single checkout. This is the canonical "what would the next settlement for this checkout pick up?" view.

The preview is per-checkout because the sweep settles one settlement per enrolled checkout, not one per merchant.

checkout_id is optional and resolves automatically:

  • If your client has exactly one checkout enrolled in the batched settlement program, you can omit checkout_id — it is resolved for you.
  • If your client has more than one enrolled checkout, checkout_id is required; omitting it returns 400.
  • If your client has no enrolled checkout (or is on the legacy 1:1 flow), the list is always empty — the sweep never settles it, so there is nothing to preview.

Query Parameters

NameTypeDescription
checkout_idintOptional. Checkout to preview. Omit it when your client has a single enrolled checkout (resolved automatically); required when it has more than one.
fromdatetimeOptional. Start of the request created_at window. ISO 8601 with a UTC offset; a datetime without an offset is rejected with 400. Default: now − 30 days (the sweep's eligibility window).
todatetimeOptional. End of the request created_at window. ISO 8601 with a UTC offset; a datetime without an offset is rejected with 400. Default: now.
limitintMaximum items per page (1–500). Default: 100.
offsetintPagination offset. Default: 0.

Example Request

const params = new URLSearchParams({
  checkout_id: '42', // optional when the client has a single enrolled checkout
  from: '2026-04-14T00:00:00Z',
  limit: '100',
  offset: '0',
});

const url = `${baseURL}/v1/settlements/pending-charges?${params}`;

const response = await fetch(url, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${access_token}`,
    "Content-Type": "application/json",
  },
});
import requests

params = {
    'checkout_id': 42,  # optional when the client has a single enrolled checkout
    'from': '2026-04-14T00:00:00Z',
    'limit': 100,
    'offset': 0,
}

url = f"{base_url}/v1/settlements/pending-charges"
headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/json',
}

response = requests.get(url, params=params, headers=headers)
params := url.Values{}
params.Add("checkout_id", "42") // optional when the client has a single enrolled checkout
params.Add("from", "2026-04-14T00:00:00Z")
params.Add("limit", "100")
params.Add("offset", "0")

u := fmt.Sprintf("%s/v1/settlements/pending-charges?%s", baseURL, params.Encode())
req, _ := http.NewRequest("GET", u, nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", access_token))

Response

{
  "items": [
    {
      "kamipay_request_id": "ptxr_01kr3m9p7n2s4d8h6e5b3t7w1k",
      "kamipay_id": "dqr_01kr3m9q5h7w2v4n6b8s3d5e9p",
      "external_id": "merchant-order-aaa-11112",
      "charged_amount": 5.28,
      "charged_currency": "BRL",
      "settlement_amount": 29750.0,
      "settlement_currency": "ARS",
      "charged_timestamp": "2026-05-14T13:21:08Z"
    },
    {
      "kamipay_request_id": "ptxr_01kr4a2x6m1d8h6e5b3t7w1ku",
      "kamipay_id": "dqr_01kr4a2y9n3s7v4n6b8s3d5e9q",
      "external_id": "merchant-order-aaa-11113",
      "charged_amount": 7.04,
      "charged_currency": "BRL",
      "settlement_amount": 39575.0,
      "settlement_currency": "ARS",
      "charged_timestamp": "2026-05-14T14:02:55Z"
    }
  ],
  "totals": {
    "count": 2,
    "settlement_amount": 69325.0
  },
  "limit": 100,
  "offset": 0
}
{
  "items": [],
  "totals": {
    "count": 0,
    "settlement_amount": 0
  },
  "limit": 100,
  "offset": 0
}

checkout_id required (client has more than one enrolled checkout)

{
  "detail": "checkout_id is required: this client has multiple checkouts enrolled in batched settlement"
}

Missing UTC offset

{
  "detail": "from must include a UTC offset (e.g. 2026-05-01T00:00:00Z)"
}

Date range validation error

{
  "detail": "Date range cannot exceed 31 days"
}
{
  "detail": "Incorrect Credentials"
}

Item Fields

Items are ordered by charged_timestamp ascending (oldest pending charge first). The item shape is the same charge core the Settlement Transactions endpoint returns (minus the parent-settlement fields), so you can relate a pending charge to the transaction that eventually settles it.

FieldTypeDescription
kamipay_request_idstringPay-in request identifier.
kamipay_idstringQR/transaction identifier.
external_idstring | nullYour own identifier for the charge, as supplied at checkout time. null if you did not provide one. This is the same value emitted on the per-charge done webhook. Use it as your primary reconciliation key.
charged_amountnumberAmount charged to the payor, typically BRL.
charged_currencystringCurrency code (alpha3) of charged_amount, e.g. BRL.
settlement_amountnumberThe ARS amount the charge was quoted at — the value the next batched settlement will disburse for it.
settlement_currencystringCurrency code (alpha3) of settlement_amountARS for the batched settlement pool.
charged_timestampdatetimeWhen the pay-in request (QR/charge) was created.

Totals Block

Totals are computed across the entire filtered set, not just the current page.

FieldTypeDescription
countintegerNumber of pending charges in the filtered set.
settlement_amountnumberSum of settlement_amount across the filtered set.

Totals reflect the full filtered set (not just the current page), so you can gauge the size of the upcoming settlement without paging through every item.


Get Settlement Detail

GET /v1/settlements/{settlement_id}

Return one settlement with its per-charge breakdown (charges[]). The charges[] shape matches the batched-program settlement.settled webhook contract, so if you ever miss a webhook delivery you can fetch the same data over REST. This endpoint works for any settlement (batched or legacy 1:1) regardless of whether a webhook was sent.

Path Parameters

NameTypeDescription
settlement_idintegerRequired. The settlement_id of the settlement.

Query Parameters

NameTypeDescription

Example Request

const settlement_id = 12345;

const url = `${baseURL}/v1/settlements/${settlement_id}`;

const response = await fetch(url, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${access_token}`,
    "Content-Type": "application/json",
  },
});
import requests

settlement_id = 12345

url = f"{base_url}/v1/settlements/{settlement_id}"
headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/json',
}

response = requests.get(url, headers=headers)
settlementID := 12345

u := fmt.Sprintf("%s/v1/settlements/%d", baseURL, settlementID)
req, _ := http.NewRequest("GET", u, nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", access_token))

Response

{
  "settlement_id": 12345,
  "settlement_provider_name": "provider_x",
  "provider_settlement_id": "psid_8f3c1d2a9e",
  "external_settlement_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f",
  "amount": 1234567.89,
  "currency": "ARS",
  "address_to": "0x7a3F9b2C1e8D5462bA9c7F3e6D85907df41A2c3B",
  "address_from": "0x4f2D9aB16C3eD7849bA5d3E2F8C19075aE63C9B1",
  "settlement_message": null,
  "settled_at": "2026-05-14T15:00:42Z",
  "created_at": "2026-05-14T14:55:18Z",
  "status": "DONE",
  "charges": [
    {
      "kamipay_id": "dqr_01kr3m9q5h7w2v4n6b8s3d5e9p",
      "external_id": "merchant-order-aaa-11112",
      "kamipay_request_id": "ptxr_01kr3m9p7n2s4d8h6e5b3t7w1k",
      "charged_amount": 5.28,
      "charged_currency": "BRL",
      "settlement_amount": 29750.0,
      "settlement_currency": "ARS"
    },
    {
      "kamipay_id": "dqr_01kr4a2y9n3s7v4n6b8s3d5e9q",
      "external_id": "merchant-order-aaa-11113",
      "kamipay_request_id": "ptxr_01kr4a2x6m1d8h6e5b3t7w1ku",
      "charged_amount": 7.04,
      "charged_currency": "BRL",
      "settlement_amount": 39575.0,
      "settlement_currency": "ARS"
    }
  ]
}
{
  "detail": "Incorrect Credentials"
}

Returned when the settlement does not exist or belongs to another merchant.

{
  "detail": "Settlement not found"
}

Detail Fields

The top-level fields are the same as a settlement row from GET /v1/settlements, plus a charges array.

FieldTypeDescription
chargesarrayAll charges included in this settlement.

charges[] Fields

FieldTypeDescription
kamipay_idstringThe QR/transaction kamiPay identifier of the original charge.
external_idstring | nullYour own identifier for the charge, as supplied at checkout time. null if you did not provide one. Use it as your primary reconciliation key.
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).

Settlement Status Values

StatusDescription
CREATEDSettlement record created; the outbound transfer has not yet been issued.
PROCESSINGOutbound transfer issued; waiting for the provider to confirm.
DONEProvider confirmed the transfer; funds are with the merchant.
CANCELEDSettlement was cancelled before completion (typically because the underlying transfer was not attempted).
FAILEDOutbound transfer or provider confirmation failed irrecoverably. Manual ops resolution.

Use Cases

Daily reconciliation

  1. GET /v1/settlements for the day's settled_at range.
  2. For each settlement, capture settlement_id, amount, currency, and settled_at for your books.
  3. GET /v1/settlements/{settlement_id} (or GET /v1/settlements/transactions?settlement_id=<id>) to enumerate the charges included in each settlement. Each charge carries your external_id — use it to match charges back to your own records.

Previewing the pending pool

If you are enrolled in the batched settlement program and want to know which charges are currently queued for the next run:

  1. GET /v1/settlements/pending-charges — with a single enrolled checkout you can omit checkout_id; with more than one, pass ?checkout_id=<c> (one call per checkout, since the sweep settles per checkout).
  2. Read totals.count for the number of charges in that checkout's pool and totals.settlement_amount for the expected disbursement of its next settlement.
  3. Iterate items[] (paging with limit/offset) for per-charge details; match each item's external_id to your own records.

On this page