> ## 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.

# Issue account detail

> Requests a bank or blockchain funding detail. Provisioning can complete asynchronously.

## 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 a `503` 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.

<Warning>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.</Warning>

## 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. |


## OpenAPI

````yaml /openapi.json post /v1/accounts/{accountId}/details
openapi: 3.1.0
info:
  title: Sakawave Business API
  version: 1.0.0-draft
  summary: >-
    Accounts, transfers, FX quotes, and transaction reconciliation for Sakawave
    businesses.
  description: >-
    Generated from the public NestJS controllers and their Zod request schemas.
    Public URLs and policy statements remain draft until the Phase 1 contract
    checklist is approved.
  contact:
    name: Sakawave API Support
    email: api-support@sakawave.example
servers:
  - url: https://sandbox-api.sakawave.example
    description: Sandbox (default)
  - url: https://api.sakawave.example
    description: Live
security:
  - bearerAuth: []
tags:
  - name: Bill payments
    description: Browse the biller catalog, validate a customer, and pay a bill.
  - name: Cards
    description: Issue, fund, and control USD virtual and disposable cards.
  - name: eSIM
    description: >-
      Sell mobile data abroad: browse the catalog, buy an eSIM, and manage it
      once installed.
  - name: Currencies and countries
    description: >-
      Reference data: what may be held, what may be sent where, and which
      jurisdictions are served.
  - name: Customers
    description: Create and manage customers of your business.
  - name: Accounts
    description: >-
      Open business and customer accounts, and issue the destinations that fund
      them.
  - name: Transfer destinations
    description: Resolve temporary destinations and saved recipients.
  - name: FX quotes
    description: Create and retrieve time-limited cross-currency quotes.
  - name: Transfers
    description: Preview, create, and monitor transfers.
  - name: Transactions
    description: Read ledger entries for reconciliation.
paths:
  /v1/accounts/{accountId}/details:
    post:
      tags:
        - Accounts
      summary: Issue account detail
      description: >-
        Requests a bank or blockchain funding detail. Provisioning can complete
        asynchronously.
      operationId: issueAccountDetail
      parameters:
        - name: accountId
          in: path
          required: true
          description: Account ULID.
          schema:
            $ref: '#/components/schemas/Ulid'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                chain:
                  type: string
                  enum:
                    - ETH_MAINNET
                    - SOLANA_MAINNET
                    - BASE_MAINNET
                    - POLYGON_MAINNET
              description: >-
                A chain is required for a crypto account and rejected for a fiat
                account.
            example:
              chain: BASE_MAINNET
      responses:
        '201':
          description: Successful response.
          headers:
            X-Request-ID:
              description: >-
                The request identifier assigned by Sakawave. Include it in
                support requests.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccountDetail'
              example:
                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'
        '202':
          description: >-
            Provisioning is still running. No detail is returned yet; poll GET
            /v1/accounts/{accountId}/details until it appears.
          headers:
            X-Request-ID:
              description: >-
                The request identifier assigned by Sakawave. Include it in
                support requests.
              schema:
                type: string
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/AuthenticationError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/NotFoundError'
        '409':
          $ref: '#/components/responses/ConflictError'
        '429':
          $ref: '#/components/responses/RateLimitError'
        '500':
          $ref: '#/components/responses/ServerError'
        '503':
          $ref: '#/components/responses/ProviderUnavailableError'
      security:
        - bearerAuth: []
components:
  schemas:
    Ulid:
      type: string
      minLength: 26
      maxLength: 26
      pattern: ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
      example: 01K2Z3AB4C5D6E7F8G9HJKMNPQ
      description: A 26-character ULID.
    AccountDetail:
      type: object
      additionalProperties: false
      properties:
        account:
          type: object
          additionalProperties: false
          properties:
            id:
              $ref: '#/components/schemas/Ulid'
              description: >-
                The account this detail funds. After a `202`, this is how you
                match the `account.detail.created` webhook to your account.
            currency:
              type: string
              description: The account’s currency.
            name:
              type: string
              description: Your label for the account.
          required:
            - id
            - currency
            - name
        beneficiary:
          type: object
          additionalProperties: false
          properties:
            name:
              anyOf:
                - type: string
                  description: >-
                    The name a payer sends to: the account holder as the bank
                    knows them.
                - type: 'null'
            address:
              anyOf:
                - type: string
                  description: >-
                    The account holder’s address, for corridors that ask for
                    one, such as a USD wire.
                - type: 'null'
          required:
            - name
            - address
        details:
          type: object
          additionalProperties: false
          properties:
            id:
              $ref: '#/components/schemas/Ulid'
            type:
              type: string
              enum:
                - bank_account
                - mobile_money
                - wallet_address
              description: >-
                Follows from the account’s category. A fiat account gets a bank
                destination; a crypto account gets an on-chain address.
            domestic:
              anyOf:
                - type: object
                  additionalProperties: false
                  properties:
                    accountNumber:
                      type: string
                      description: >-
                        The destination itself: a NUBAN, a handle, or a local
                        account number. Publish this to whoever is funding the
                        account.
                    routingNumber:
                      anyOf:
                        - type: string
                          description: >-
                            ABA routing number, for a USD account. Null
                            elsewhere.
                        - type: 'null'
                    sortCode:
                      anyOf:
                        - type: string
                          description: UK sort code, for a GBP account. Null elsewhere.
                        - type: 'null'
                    bankName:
                      anyOf:
                        - type: string
                          description: >-
                            The issuing institution, when the provider names
                            one.
                        - type: 'null'
                    bankAddress:
                      anyOf:
                        - type: string
                          description: The issuing institution’s address.
                        - type: 'null'
                  required:
                    - accountNumber
                    - routingNumber
                    - sortCode
                    - bankName
                    - bankAddress
                  description: >-
                    What a payer in the account’s own currency zone sends to.
                    Present for every bank and mobile-money destination; null
                    for an on-chain address.
                - type: 'null'
            international:
              anyOf:
                - type: object
                  additionalProperties: false
                  properties:
                    iban:
                      anyOf:
                        - type: string
                          description: The IBAN, where the account has one.
                        - type: 'null'
                    swiftCode:
                      anyOf:
                        - type: string
                          description: The SWIFT/BIC an international wire is sent to.
                        - type: 'null'
                    accountNumber:
                      type: string
                    sortCode:
                      anyOf:
                        - type: string
                        - type: 'null'
                    bankName:
                      anyOf:
                        - type: string
                        - type: 'null'
                    bankAddress:
                      anyOf:
                        - type: string
                          description: >-
                            The issuing institution’s address, which an
                            international wire asks for.
                        - type: 'null'
                  required:
                    - iban
                    - swiftCode
                    - accountNumber
                    - sortCode
                    - bankName
                    - bankAddress
                  description: >-
                    What a payer abroad sends to. Present only when the account
                    was issued an IBAN or SWIFT code; null for NGN and USD.
                - type: 'null'
            wallet:
              anyOf:
                - type: object
                  additionalProperties: false
                  properties:
                    chain:
                      anyOf:
                        - type: string
                          description: >-
                            The network the address is on, such as
                            `BASE_MAINNET`.
                        - type: 'null'
                    address:
                      type: string
                      description: The on-chain address.
                  required:
                    - chain
                    - address
                  description: Present only for a stablecoin account.
                - type: 'null'
            paymentRails:
              type: array
              items:
                type: string
              description: >-
                How the destination can be paid into, such as `ach_push` or
                `wire`. Empty when the provider does not say.
            providerBacked:
              type: boolean
              description: >-
                Whether an integration issued this destination. Which one stays
                private; only an issued destination can be reconciled against
                inbound funds.
            createdAt:
              $ref: '#/components/schemas/IsoTimestamp'
          required:
            - id
            - type
            - domestic
            - international
            - wallet
            - paymentRails
            - providerBacked
            - createdAt
      required:
        - account
        - beneficiary
        - details
    IsoTimestamp:
      type: string
      format: date-time
      example: '2026-08-14T09:00:00.000Z'
    RequestError:
      anyOf:
        - $ref: '#/components/schemas/ValidationError'
        - $ref: '#/components/schemas/Error'
      description: >-
        Either a field-level validation failure carrying an errors array, or a
        rule the request broke, identified by code.
    Error:
      type: object
      additionalProperties: true
      properties:
        statusCode:
          type: integer
        message:
          type: string
          description: >-
            A sentence suitable for a log or a support conversation, not for a
            user interface.
        code:
          type: string
          description: >-
            The stable machine-readable reason. Branch on this, never on the
            message.
      required:
        - message
        - code
      description: >-
        An error body. Individual errors may add context fields alongside these
        three.
    ForbiddenError:
      type: object
      additionalProperties: false
      properties:
        statusCode:
          const: 403
        message:
          type: string
        code:
          type: string
          enum:
            - kyc_approval_required
            - international_questionnaire_required
            - international_account_approval_required
            - international_customer_wallet_not_supported
          description: >-
            Present on verification, international access, and customer wallet
            refusals. Absent on tier denials, which carry `reason` instead.
        reason:
          type: string
          enum:
            - currency_not_available
            - feature_not_available
          description: Present on tier denials. Absent on every other refusal.
        currency:
          $ref: '#/components/schemas/AssetCode'
        feature:
          type: string
        currentTier:
          type: integer
          enum:
            - 1
            - 2
            - 3
        requiredTier:
          type: integer
          enum:
            - 1
            - 2
            - 3
      required:
        - statusCode
        - message
    RateLimitError:
      type: object
      additionalProperties: false
      properties:
        statusCode:
          const: 429
        message:
          type: string
        code:
          const: api_key_rate_limit_exceeded
        retryAfterSeconds:
          type: integer
          minimum: 1
      required:
        - statusCode
        - message
        - code
        - retryAfterSeconds
    ValidationError:
      type: object
      additionalProperties: false
      properties:
        statusCode:
          const: 400
        message:
          type: string
          description: The single failure, or a count when more than one field failed.
        code:
          const: validation_failed
        errors:
          type: array
          items:
            $ref: '#/components/schemas/ValidationFailure'
      required:
        - statusCode
        - message
        - code
        - errors
    AssetCode:
      type: string
      enum:
        - NGN
        - USD
        - GBP
        - EUR
        - GHS
        - ZAR
        - UGX
        - CAD
        - MXN
        - AED
        - BTC
        - ETH
        - USDT
        - USDC
    ValidationFailure:
      type: object
      additionalProperties: false
      properties:
        message:
          type: string
        field:
          type: string
        code:
          type: string
        path:
          type: array
          items:
            anyOf:
              - type: string
              - type: integer
      required:
        - message
        - field
        - code
        - path
  responses:
    ValidationError:
      description: >-
        The request was rejected before it was acted on. Field failures list
        every failing field.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RequestError'
          example:
            statusCode: 400
            message: Must be a valid identifier.
            code: validation_failed
            errors:
              - message: Must be a valid identifier.
                field: accountId
                code: custom
                path:
                  - accountId
    AuthenticationError:
      description: >-
        The API key is missing, invalid, inactive, expired, for another
        environment, or blocked by its IP allowlist.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 401
            code: INVALID_API_KEY
            message: Invalid or inactive API credentials.
    ForbiddenError:
      description: >-
        Authenticated, but not permitted to hold or use this currency.
        Entitlement refusals carry `code`; tier denials carry `reason`,
        `currentTier`, and `requiredTier` instead.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ForbiddenError'
          example:
            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
    NotFoundError:
      description: The requested resource was not found for this business.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 404
            code: resource_not_found
            message: Resource not found.
    ConflictError:
      description: The request conflicts with the current resource state.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 409
            code: resource_conflict
            message: The request conflicts with the current resource state.
    RateLimitError:
      description: The API key rate limit was exceeded.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitError'
          example:
            statusCode: 429
            message: Too many API requests. Try again later.
            code: api_key_rate_limit_exceeded
            retryAfterSeconds: 17
    ServerError:
      description: An unexpected server error occurred.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 500
            code: internal_server_error
            message: An unexpected error occurred.
    ProviderUnavailableError:
      description: >-
        A payment provider could not be reached. The request had no effect;
        retry shortly.
      headers:
        X-Request-ID:
          description: >-
            The request identifier assigned by Sakawave. Include it in support
            requests.
          schema:
            type: string
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 503
            code: provider_unavailable
            message: We could not issue an account number just now. Try again shortly.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Sakawave API key
      description: >-
        A secret API key, sent as `Authorization: Bearer sk_test_…` in sandbox
        or `Bearer sk_live_…` in live.

````

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