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
- As charges complete, each one is marked
doneand you receive the usual charge webhooks. The charge enters the "pending charges" pool for the next settlement cycle. - On a recurring schedule, kamiPay groups all of your
donecharges that have no associated settlement into a single settlement. - When the settlement is confirmed, you receive a
settlement.settledwebhook listing every charge it included (with yourexternal_idper 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
| Name | Type | Description |
|---|---|---|
| start_date | datetime | Required. 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_date | datetime | Required. 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. |
| limit | int | Maximum results (1–1000). Default: 100. |
| offset | int | Pagination 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
| Field | Type | Description |
|---|---|---|
settlement_id | integer | Unique identifier of the settlement. |
settlement_provider_name | string | Provider that executed the transfer. |
provider_settlement_id | string | null | Provider-side identifier for the transfer. null until the provider confirms. |
external_settlement_id | string | null | Auxiliary external reference (e.g. on-chain transaction hash for blockchain settlements, originating vault tx hash for off-chain settlements). |
amount | number | Amount delivered to your address_to, in currency units. |
currency | string | Currency code (alpha3) of amount, e.g. ARS. |
address_to | string | Destination wallet/account where the funds were delivered. |
address_from | string | Originating kamiPay vault address. |
settlement_message | string | null | Optional message attached to the transfer. |
settled_at | datetime | null | Timestamp the settlement was confirmed. null while still in flight. |
created_at | datetime | Settlement-row creation time. |
status | string | null | Current 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
| Name | Type | Description |
|---|---|---|
| start_date | datetime | Required. 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_date | datetime | Required. End of the range. ISO 8601 with a UTC offset. A datetime without an offset is rejected with 400. |
| settlement_id | integer | Filter to a specific settlement id. |
| limit | int | Maximum results (1–1000). Default: 100. |
| offset | int | Pagination 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
| Field | Type | Description |
|---|---|---|
settlement_id | integer | Parent settlement id. |
kamipay_request_id | string | Pay-in request identifier (Pix id). |
kamipay_id | string | The QR/transaction kamiPay identifier (matches the value returned at QR creation). |
external_id | string | null | Your 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_amount | number | Amount charged to the payor, typically BRL. |
charged_currency | string | Currency code (alpha3) of charged_amount, e.g. BRL. |
settlement_amount | number | null | The 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_currency | string | null | Currency code (alpha3) of settlement_amount — the quote currency of the checkout quote layout (e.g. ARS, USDt, BRL). |
charged_timestamp | datetime | null | When the pay-in request (QR/charge) was created. |
created_at | datetime | Timestamp the charge was associated with the settlement. |
settlement_provider_name | string | Provider of the parent settlement. |
settled_at | datetime | null | Settled-at timestamp of the parent settlement. null while in flight. |
provider_settlement_id | string | null | Provider-side identifier of the parent settlement. |
external_settlement_id | string | null | Auxiliary 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_idis required; omitting it returns400. - 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
| Name | Type | Description |
|---|---|---|
| checkout_id | int | Optional. Checkout to preview. Omit it when your client has a single enrolled checkout (resolved automatically); required when it has more than one. |
| from | datetime | Optional. 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). |
| to | datetime | Optional. End of the request created_at window. ISO 8601 with a UTC offset; a datetime without an offset is rejected with 400. Default: now. |
| limit | int | Maximum items per page (1–500). Default: 100. |
| offset | int | Pagination 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.
| Field | Type | Description |
|---|---|---|
kamipay_request_id | string | Pay-in request identifier. |
kamipay_id | string | QR/transaction identifier. |
external_id | string | null | Your 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_amount | number | Amount charged to the payor, typically BRL. |
charged_currency | string | Currency code (alpha3) of charged_amount, e.g. BRL. |
settlement_amount | number | The ARS amount the charge was quoted at — the value the next batched settlement will disburse for it. |
settlement_currency | string | Currency code (alpha3) of settlement_amount — ARS for the batched settlement pool. |
charged_timestamp | datetime | When the pay-in request (QR/charge) was created. |
Totals Block
Totals are computed across the entire filtered set, not just the current page.
| Field | Type | Description |
|---|---|---|
count | integer | Number of pending charges in the filtered set. |
settlement_amount | number | Sum 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
| Name | Type | Description |
|---|---|---|
| settlement_id | integer | Required. The settlement_id of the settlement. |
Query Parameters
| Name | Type | Description |
|---|
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.
| Field | Type | Description |
|---|---|---|
charges | array | All charges included in this settlement. |
charges[] Fields
| Field | Type | Description |
|---|---|---|
kamipay_id | string | The QR/transaction kamiPay identifier of the original charge. |
external_id | string | null | Your 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_id | string | Pay-in request identifier (Pix id). |
charged_amount | number | Amount charged to the payor, typically BRL. |
charged_currency | string | Currency code (alpha3) of charged_amount, e.g. BRL. |
settlement_amount | number | null | The 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_currency | string | null | Currency code (alpha3) of settlement_amount — the quote currency of the checkout quote layout (e.g. ARS, USDt, BRL). |
Settlement Status Values
| Status | Description |
|---|---|
CREATED | Settlement record created; the outbound transfer has not yet been issued. |
PROCESSING | Outbound transfer issued; waiting for the provider to confirm. |
DONE | Provider confirmed the transfer; funds are with the merchant. |
CANCELED | Settlement was cancelled before completion (typically because the underlying transfer was not attempted). |
FAILED | Outbound transfer or provider confirmation failed irrecoverably. Manual ops resolution. |
Use Cases
Daily reconciliation
GET /v1/settlementsfor the day'ssettled_atrange.- For each settlement, capture
settlement_id,amount,currency, andsettled_atfor your books. GET /v1/settlements/{settlement_id}(orGET /v1/settlements/transactions?settlement_id=<id>) to enumerate the charges included in each settlement. Each charge carries yourexternal_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:
GET /v1/settlements/pending-charges— with a single enrolled checkout you can omitcheckout_id; with more than one, pass?checkout_id=<c>(one call per checkout, since the sweep settles per checkout).- Read
totals.countfor the number of charges in that checkout's pool andtotals.settlement_amountfor the expected disbursement of its next settlement. - Iterate
items[](paging withlimit/offset) for per-charge details; match each item'sexternal_idto your own records.