The routes
| Route | Scope | What it does |
|---|---|---|
| GET /v1/payout-accounts | moneyOut:read | Lists the payout accounts the school has registered, so you can pick a destination id. |
| POST /v1/withdrawals | moneyOut:write | Requests a withdrawal from the school wallet to one registered payout account. |
| GET /v1/withdrawals/{reference} | moneyOut:read | Reads a withdrawal and its status. |
| POST /v1/payroll/runs/{id}/pay | moneyOut:write | Disburses an approved payroll run. |
Every check, before anything runs
Every money-out call, reads included, passes these checks before its handler runs. A refusal moves nothing and is written to the school audit log.
- A live secret key. Test keys are refused with money_out_live_key_required, and there is no test response: money-out has no test mode.
- An IP allowlist on the app, with the caller's address on it. An empty allowlist refuses every call with money_out_ip_not_allowlisted.
- The moneyOut scope granted by a school administrator, who confirms the grant with a step-up check. Without it the answer is money_out_grant_required.
- An Idempotency-Key on every write. Without one the answer is money_out_idempotency_key_required.
- The app itself, never a person. An end-user token is refused with money_out_end_user_token_not_allowed, and a publishable key never reaches these routes.
- An app registered by the school. Organization apps can read across their branches but cannot move money: money_out_org_app_not_allowed.
Payout accounts are registered in the dashboard
The API cannot add or change a payout account. A school administrator registers each destination account in the dashboard, where it is verified. A withdrawal names one of those accounts by id and can go nowhere else.
Requesting a withdrawal
curl -X POST https://api.klasstack.com/v1/withdrawals \
-H "Authorization: Bearer ks_live_sk_your_key_here" \
-H "Idempotency-Key: 6d0c7f0e-2a41-4b8e-9f3a-1c2d3e4f5a6b" \
-H "Content-Type: application/json" \
-d '{"payoutAccountId": 12, "amount": 50000, "narration": "September supplier run"}'amount is in naira, not kobo. The response carries a reference and the status PENDING_APPROVAL.
The 24-hour approval queue
- An accepted withdrawal does not pay out at once. It enters the same 24-hour approval queue as a withdrawal made in the dashboard.
- Statuses run PENDING_APPROVAL, then APPROVED, then COMPLETED. A withdrawal can instead end REJECTED, CANCELLED, FAILED or REVERSED.
- A withdrawal nobody approves within 24 hours is declined.
- Follow progress with GET /v1/withdrawals/{reference}, or subscribe to the withdrawal webhook events rather than polling.