> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sakawave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Account use cases

> Four flows — opening a payable account, giving customers wallets, separating your money from theirs, and reconciling balances.

## 1. Open an account that can actually be paid

Opening is one call; becoming payable is two. Do not treat the `201` from `POST /v1/accounts` as the end of the flow.

```bash theme={null}
ACCOUNT=$(curl --silent --show-error --fail-with-body \
  --request POST \
  --url "$SAKAWAVE_BASE_URL/v1/accounts" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "name": "Operating NGN", "type": "operational", "currency": "NGN" }')

ACCOUNT_ID=$(printf '%s' "$ACCOUNT" | jq --raw-output '.id')

curl --request POST \
  --url "$SAKAWAVE_BASE_URL/v1/accounts/$ACCOUNT_ID/details" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{}'
```

**The branch that matters** is the second call's status:

```ts theme={null}
const issued = await post(`/v1/accounts/${accountId}/details`, {});

if (issued.status === 201) {
  // The whole detail: a payer may need the sort code, IBAN or SWIFT code
  // from details.domestic or details.international too.
  await publishFundingInstructions(accountId, issued.body);
} else if (issued.status === 202) {
  // Normal. The account.detail.created webhook will carry it.
  await markAwaitingDestination(accountId);
} else if (issued.status === 503) {
  // The provider was unreachable and nothing was created. Retry with backoff —
  // issuance resumes rather than duplicating.
  await scheduleRetry(accountId);
}
```

Never show a customer a funding instruction built from an account that is still `transferReady: false`.

## 2. Give each customer their own wallet

A wallet is an ordinary account with `customerId` set.

```bash theme={null}
curl --request POST \
  --url "$SAKAWAVE_BASE_URL/v1/accounts" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
  --header "Content-Type: application/json" \
  --data "{
    \"name\": \"Ada NGN wallet\",
    \"type\": \"operational\",
    \"currency\": \"NGN\",
    \"customerId\": \"$CUSTOMER_ID\"
  }"
```

Three constraints govern this flow, all of them consequences of customers being tier 1:

* **NGN only.** Any other currency is `403 international_customer_wallet_not_supported`, even if your own accounts in that currency work.
* **No crypto.** Crypto needs tier 2.
* **₦50,000 ceiling**, for both the balance and the daily outflow.

If those do not fit, the alternative shape is one account of your own per currency with per-user balances in your ledger — you keep your business's tier limits and lose per-user account numbers.

## 3. Separate your money from your customers'

Never infer ownership from the account name. Filter on it.

```http theme={null}
GET /v1/accounts?customerId:isnull=true       # your own accounts
GET /v1/accounts?isWalletAccount=true         # every customer wallet
GET /v1/accounts?customerId=01K2Z3AB4C5D6E7F8G9HJKMNPQ   # one customer's wallets
```

A safeguarding-style reconciliation — do your customers' wallets add up to what you are holding for them? — is two queries and a sum:

```bash theme={null}
# Total held on behalf of customers, in minor units.
curl --silent \
  --url "$SAKAWAVE_BASE_URL/v1/accounts?isWalletAccount=true&currency=NGN&pageSize=100" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
| jq '[.items[].balance | tonumber] | add'
```

<Warning>
  `balance` is a **string of minor units**, and it can exceed what a JSON number holds exactly. Sum it with a big-integer or decimal type in production code — the `jq` line above is fine for a sandbox check and wrong for a ledger.
</Warning>

Filters on `balance` are in minor units too. `balance:gt=0` is "has any money"; `balance:gte=100000` on an NGN account means ₦1,000, not ₦100,000.

## 4. Reconcile

Balances are a snapshot; [transactions](/tutorials/reconcile-transactions) are the record. Use both, for different jobs.

| Question | Endpoint |
| - | - |
| What is in this account right now? | `GET /v1/accounts/{accountId}` |
| What moved, and why? | `GET /v1/transactions?account=…` |
| Which accounts are dormant? | `GET /v1/accounts?balance=0&status=active` |
| Which are stuck without a destination? | List accounts, then check `transferReady` |

Finding accounts that never became payable is worth a scheduled job — a `202` that never completed is silent otherwise:

```bash theme={null}
curl --silent \
  --url "$SAKAWAVE_BASE_URL/v1/accounts?status=active&pageSize=100" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
| jq '[.items[] | select(.transferReady == false)] | map({id, name, createdAt})'
```

Anything in that list older than a few minutes needs its detail re-issued.

## What you cannot do here

* **Close an account.** There is no endpoint. It is a dashboard action, and a frozen account cannot be closed at all until the hold is lifted.
* **Rename an account or change its metadata.** Both are fixed at creation.
* **Choose your own account number.** The provider allocates it.
* **Move an account between customers.** `customerId` is set once.

The first of these is the one that bites: an offboarding flow built entirely on this API stops one step short of done, because archiving a customer requires their wallets to be closed first, and closing is not something this API can do.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.