curl --request GET \
--url https://api.pay.aptahq.com/v1/float \
--header 'X-AptaPay-Signature: <api-key>'const options = {method: 'GET', headers: {'X-AptaPay-Signature': '<api-key>'}};
fetch('https://api.pay.aptahq.com/v1/float', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.pay.aptahq.com/v1/float"
headers = {"X-AptaPay-Signature": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text){
"code": 200,
"message": "OK",
"data": [
{
"currency": "UGX",
"available_minor": 1520000,
"reserved_minor": 30000,
"collected_minor": 2450000,
"paid_out_minor": 900000,
"provider_fees_minor": 56300
},
{
"currency": "KES",
"available_minor": 42000,
"reserved_minor": 0,
"collected_minor": 42000,
"paid_out_minor": 0,
"provider_fees_minor": 966
}
]
}{
"code": 200,
"message": "OK",
"data": "<unknown>",
"error": {
"code": "payout_rejected_by_provider",
"message": "<string>",
"terminal": true,
"retriable": true,
"ambiguous": true,
"details": {}
}
}Your own float, per currency
How much YOU can actually pay out, per currency. This is the number a payout is checked against, so it is the one to show on a dashboard and the one to check before a batch run.
This is not GET /v1/balances, and the difference matters. /v1/balances reports the PROVIDER’s wallet — a single pool that every tenant on this gateway shares. Most of what it shows is not yours. This endpoint reports your own claim on that pool. A tenant holding 2,000 UGX of float can read a 4,000,000 UGX pooled balance from /v1/balances and still have a payout refused with insufficient_tenant_float. If that has ever looked inexplicable, this endpoint is the explanation.
available_minor is what a payout draws from. It grows when a collection settles and shrinks when a payout is claimed. A payout larger than it is refused before the provider is ever contacted — your own float is checked first, so another tenant draining the shared wallet can never spend money you collected.
reserved_minor is in flight, not lost. A payout moves money from available to reserved at claim time and out of reserved when it settles; if it fails terminally the money returns to available. Money sitting in reserved is still in the wallet — it is spoken for, not spent.
provider_fees_minor is a lifetime total of fees in BOTH directions, and it is already reflected in available_minor. Never subtract it yourself. The two kinds of fee behave differently, which is why the total alone cannot tell you what to do with it:
-
A collection fee was never yours to lose. Eversend appends it to what the customer pays and keeps it, so your wallet is credited the NET amount and there is nothing further to subtract.
-
A payout fee IS yours: the provider debits it from the wallet on top of the amount delivered (a UGX 10,000 bank payout costs UGX 15,400 at current bank rates). It has already been deducted from
available_minorwhen the payout settled.
So provider_fees_minor is a reporting figure for reconciling against a provider statement. Subtracting it from available_minor would double-count the payout half and wrongly charge you for the collection half.
Read-only, and not a reservation. These figures are maintained for reporting; the authoritative check happens inside the payout transaction. Between reading this and calling POST /v1/payouts, your own concurrent payouts can move it. Treat a shortfall here as a reason not to try, never a success here as a guarantee.
Available to every tenant. Your own float position needs no capability grant — it is your data, and it touches no provider.
An empty array means you hold no float in any currency yet, which is the normal state before your first collection settles.
curl --request GET \
--url https://api.pay.aptahq.com/v1/float \
--header 'X-AptaPay-Signature: <api-key>'const options = {method: 'GET', headers: {'X-AptaPay-Signature': '<api-key>'}};
fetch('https://api.pay.aptahq.com/v1/float', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.pay.aptahq.com/v1/float"
headers = {"X-AptaPay-Signature": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text){
"code": 200,
"message": "OK",
"data": [
{
"currency": "UGX",
"available_minor": 1520000,
"reserved_minor": 30000,
"collected_minor": 2450000,
"paid_out_minor": 900000,
"provider_fees_minor": 56300
},
{
"currency": "KES",
"available_minor": 42000,
"reserved_minor": 0,
"collected_minor": 42000,
"paid_out_minor": 0,
"provider_fees_minor": 966
}
]
}{
"code": 200,
"message": "OK",
"data": "<unknown>",
"error": {
"code": "payout_rejected_by_provider",
"message": "<string>",
"terminal": true,
"retriable": true,
"ambiguous": true,
"details": {}
}
}Authorizations
v1={hex HMAC-SHA256} over the ten-field canonical string. Sent alongside X-AptaPay-Key, X-AptaPay-Timestamp and X-AptaPay-Nonce — all four are required. OpenAPI can only model one header per scheme, so the other three are described in the Authentication section above.
Response
One entry per currency this tenant holds a position in.
The single response envelope, used by EVERY endpoint so a consuming app parses one shape everywhere.
200
"OK"
Present on success. Shape varies per endpoint.
Machine-readable error classification. EXACTLY ONE of terminal/retriable/ ambiguous is true. laces_api inferred this from the HTTP status and got it wrong, stranding real money twice (2026-08-15, 2026-08-16). Branch on these booleans, never on the status code:
terminal -> the provider refused. Reverse the debit and tell the user. retriable -> safe to retry with the SAME Idempotency-Key. ambiguous -> the outcome is UNKNOWN. Do NOT reverse. Poll instead.
Show child attributes
Show child attributes