Issue account detail
curl --request POST \
--url https://sandbox-api.sakawave.example/v1/accounts/{accountId}/details \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"chain": "BASE_MAINNET"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({chain: 'BASE_MAINNET'})
};
fetch('https://sandbox-api.sakawave.example/v1/accounts/{accountId}/details', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://sandbox-api.sakawave.example/v1/accounts/{accountId}/details"
payload = { "chain": "BASE_MAINNET" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"account": {
"id": "01K2Z3AB4D5E6F7G8H9JKMNPQR",
"currency": "NGN",
"name": "Collections"
},
"beneficiary": {
"name": "Ada Okafor",
"address": null
},
"details": {
"id": "01K2Z3AB4P5Q6R7S8TVWXYZABC",
"type": "bank_account",
"domestic": {
"accountNumber": "0123456789",
"routingNumber": null,
"sortCode": null,
"bankName": "Example Bank",
"bankAddress": null
},
"international": null,
"wallet": null,
"paymentRails": [],
"providerBacked": true,
"createdAt": "2026-08-14T09:03:00.000Z"
}
}{
"statusCode": 400,
"message": "Must be a valid identifier.",
"code": "validation_failed",
"errors": [
{
"message": "Must be a valid identifier.",
"field": "accountId",
"code": "custom",
"path": [
"accountId"
]
}
]
}{
"statusCode": 401,
"code": "INVALID_API_KEY",
"message": "Invalid or inactive API credentials."
}{
"statusCode": 403,
"message": "International wallets for your customers are not available yet. Your own approval cannot be used for a customer wallet.",
"code": "international_customer_wallet_not_supported",
"currency": "USD"
}{
"statusCode": 404,
"code": "resource_not_found",
"message": "Resource not found."
}{
"statusCode": 409,
"code": "resource_conflict",
"message": "The request conflicts with the current resource state."
}{
"statusCode": 429,
"message": "Too many API requests. Try again later.",
"code": "api_key_rate_limit_exceeded",
"retryAfterSeconds": 17
}{
"statusCode": 500,
"code": "internal_server_error",
"message": "An unexpected error occurred."
}{
"statusCode": 503,
"code": "provider_unavailable",
"message": "We could not issue an account number just now. Try again shortly."
}Accounts
Issue account detail
Requests a bank or blockchain funding detail. Provisioning can complete asynchronously.
POST
/
v1
/
accounts
/
{accountId}
/
details
Issue account detail
curl --request POST \
--url https://sandbox-api.sakawave.example/v1/accounts/{accountId}/details \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"chain": "BASE_MAINNET"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({chain: 'BASE_MAINNET'})
};
fetch('https://sandbox-api.sakawave.example/v1/accounts/{accountId}/details', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://sandbox-api.sakawave.example/v1/accounts/{accountId}/details"
payload = { "chain": "BASE_MAINNET" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"account": {
"id": "01K2Z3AB4D5E6F7G8H9JKMNPQR",
"currency": "NGN",
"name": "Collections"
},
"beneficiary": {
"name": "Ada Okafor",
"address": null
},
"details": {
"id": "01K2Z3AB4P5Q6R7S8TVWXYZABC",
"type": "bank_account",
"domestic": {
"accountNumber": "0123456789",
"routingNumber": null,
"sortCode": null,
"bankName": "Example Bank",
"bankAddress": null
},
"international": null,
"wallet": null,
"paymentRails": [],
"providerBacked": true,
"createdAt": "2026-08-14T09:03:00.000Z"
}
}{
"statusCode": 400,
"message": "Must be a valid identifier.",
"code": "validation_failed",
"errors": [
{
"message": "Must be a valid identifier.",
"field": "accountId",
"code": "custom",
"path": [
"accountId"
]
}
]
}{
"statusCode": 401,
"code": "INVALID_API_KEY",
"message": "Invalid or inactive API credentials."
}{
"statusCode": 403,
"message": "International wallets for your customers are not available yet. Your own approval cannot be used for a customer wallet.",
"code": "international_customer_wallet_not_supported",
"currency": "USD"
}{
"statusCode": 404,
"code": "resource_not_found",
"message": "Resource not found."
}{
"statusCode": 409,
"code": "resource_conflict",
"message": "The request conflicts with the current resource state."
}{
"statusCode": 429,
"message": "Too many API requests. Try again later.",
"code": "api_key_rate_limit_exceeded",
"retryAfterSeconds": 17
}{
"statusCode": 500,
"code": "internal_server_error",
"message": "An unexpected error occurred."
}{
"statusCode": 503,
"code": "provider_unavailable",
"message": "We could not issue an account number just now. Try again shortly."
}Two success statuses
Branch on the status, not on the body.| Status | Meaning |
|---|---|
201 | The destination was issued. The body is the detail; publish accountNumber to whoever is funding the account. |
202 | The provider accepted the request but has not finished. There is no body yet. Poll GET /v1/accounts/{accountId}/details until it appears. |
Safe to retry
Issuance walks three steps at the provider — your business, then the account, then the destination — and skips any already taken. A retry after a half-finished attempt resumes rather than creating a second account, so a503 is safe to retry.
Verification and international access
For your own non-NGN fiat account, this is where your verification and international access are checked — opening the account checks neither. Verification is checked first. International access is one approval for your business, covering every currency. Both are checked on every request, so an approval withdrawn after the account opened stops new details being issued. NGN and crypto accounts skip both checks.Chains
chain is required for a crypto account and refused for a fiat one; the account decides which. An address is issued per chain, and one issued on the wrong network is where a deposit goes to die.
You cannot name your own account number. A destination is allocated by the provider — a client that could assert its own number could publish one it does not own and have its customers pay a stranger.
Error codes
| Status | code | Cause |
|---|---|---|
400 | chain_required | A crypto account was asked for a destination without naming a chain. |
400 | chain_not_applicable | A fiat account was given a chain. A fiat account is not reached over a chain. |
403 | kyc_approval_required | The account is in a currency other than NGN, and your verification is not approved. |
403 | international_questionnaire_required | You have not applied for international access, or your last application was withdrawn or expired. Complete the one-time questionnaire in the dashboard. |
403 | international_account_approval_required | You have applied for international access and do not hold an active approval. |
403 | international_customer_wallet_not_supported | The account is a customer wallet in a currency other than NGN. |
409 | account_not_active | The account is inactive, frozen, or closed. The current status is in the status field. |
503 | provider_unavailable | The provider could not be reached. Nothing was created; retry shortly. |
401 | INVALID_API_KEY | The key is missing, invalid, inactive, expired, from the other environment, or blocked by its IP allowlist. |
404 | account_not_found | No such account for this business. Internal platform balances are never visible, and a closed account still is. |
429 | api_key_rate_limit_exceeded | The per-minute limit for this key was exceeded. |
Authorizations
A secret API key, sent as Authorization: Bearer sk_test_… in sandbox or Bearer sk_live_… in live.
Path Parameters
Account ULID. A 26-character ULID.
Required string length:
26Pattern:
^[0-7][0-9A-HJKMNP-TV-Z]{25}$Example:
"01K2Z3AB4C5D6E7F8G9HJKMNPQ"
Body
application/json
A chain is required for a crypto account and rejected for a fiat account.
Available options:
ETH_MAINNET, SOLANA_MAINNET, BASE_MAINNET, POLYGON_MAINNET