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

# Get a customer's credit balance

> What this customer has left to spend, one entry per currency they have
ever held a balance in. Derived from the same ledger an invoice
spends, so `available` is what the next invoice in that currency will
take before charging the rest.

A new invoice spends the balance after tax and records it as
`balanceApplied`. Card verification charges and payment-link checkouts
never spend it. Voiding an invoice gives back what it spent.

An automatic charge below the minimum (the card gateway's own floor,
or the merchant's higher minimum) is never sent. The invoice is marked
paid and its amount becomes debt on the balance: `balance` goes
negative and `available` stays at 0. The next invoice in that currency
collects it, with a negative `balanceApplied` that adds to its
`amountDue`.

Promotional credit lots that are active, unexpired and not yet spent
are listed in `grants` and counted in `available`. The next invoice
spends them before tax (soonest expiry first, then promotional before
prepaid, then oldest), so they lower its VAT like a discount and show
as `grantApplied`. A lot only pays invoices in its own currency.

Prepaid credit taxed when bought (`vatTiming: at_purchase`) is spent
before tax in the same order, but only on lines taxed the way its
top-up was (the same tax category and rate, in the lot's `tax`): an
exempt, zero-rated or export line, or one after a rate change, is
billed normally. A lot bought at a price with VAT included is shown
in that price's terms in `amount`, `remaining` and `available`, so
10,000 bought with VAT inside reads 10,000 and covers 10,000 of usage
priced the same way.

Prepaid credit taxed when used (`vatTiming: at_redemption`) is spent
after tax, like cash, in `balanceApplied`: lots expiring soonest
first, then those that never expire, then the money balance last. A
top-up awaiting payment shows in `pending`, never in `available`.

`unbilled` counts usage so far in the customer's open periods
against the credit: what the invoices at their period ends would
draw if the usage stopped now, priced the way those invoices will
be. `projectedAvailable` is what would be left, the number to compare
with zero before serving a request, and `projectedShortfall` what
those invoices would still collect. Usage is only as fresh as what
you have sent, and stopping at zero is your call.

`held` is what open holds would take, priced on top of that usage
(`POST /customers/{id}/credit-holds`), and comes off
`projectedAvailable` too. To stop a customer exactly when their
credit runs out, reserve before each request instead of reading
this: a hold is refused when the credit can't cover it.

Needs the `credits:read` scope on a restricted key.




## OpenAPI

````yaml /api/openapi.yaml get /customers/{customer}/credit-balance
openapi: 3.1.0
info:
  title: Bouclay API
  version: 1.0.0
  summary: Integrator API for Bouclay subscriptions, invoices, payments, and webhooks.
  description: >
    Bouclay is managed recurring-billing infrastructure for African payment

    gateways. Integrators connect their own gateway account (Bachs, with Nomba,

    Paystack and Flutterwave in early access) and use this API to manage
    customers, catalog, subscriptions,

    entitlements, usage metering, invoices, payment-method checkout sessions,
    payments, and

    outbound event history.


    Bouclay never holds funds — every charge, refund, and payout happens on your

    own gateway account, with your own keys. Which gateway settles a given
    charge

    is Bouclay's concern, not yours: a stored card always charges through the

    gateway that issued its token.


    ## Early access


    Some of this API is documented ahead of its release, so you can build

    against it now. Those operations carry an **Early access** badge, and so

    do the request fields that depend on them (graduated, volume and package

    pricing, usage-based prices, usage thresholds). Until a feature is turned

    on for your account, its endpoints answer `404 not_found`, its fields are

    refused with a `422`, and a restricted key cannot be given its scopes.

    Ask us to turn it on. Nothing you already rely on changes when it is.
servers:
  - url: https://bouclay.com/api/v1
    description: Production
security:
  - SecretKeyAuth: []
tags:
  - name: Customers
  - name: Customer addresses
  - name: Payment methods
  - name: Entitlements
    description: >-
      Early access: answers `404 not_found` until it is turned on for your
      account.
  - name: Credits
    description: >-
      What a customer has left to spend. Money owed back to a customer is their
      balance, one per currency; their next invoice spends it after tax, like a
      payment, and collects the rest. Promotional credit you give is held in
      lots (credit grants), each with its own expiry; the next invoice spends it
      before tax, so it lowers the VAT like a discount.
  - name: Catalog
  - name: Payment links
    description: >-
      A shareable checkout page for one price, the same link a price's "Payment
      link" makes in the dashboard. Buyers enter their email and pay; a
      subscription price starts the subscription once paid.
  - name: Meters
    description: >-
      Early access: answers `404 not_found` until it is turned on for your
      account.
  - name: Usage
    description: >-
      Early access: answers `404 not_found` until it is turned on for your
      account.
  - name: Discounts
    description: >-
      Early access: answers `404 not_found` until it is turned on for your
      account.
  - name: Subscriptions
  - name: Invoices
  - name: Metrics
    description: >-
      Early access: answers `404 not_found` until it is turned on for your
      account. Revenue metrics measured from each subscription's monthly
      recurring amount (MRR). `active` and `past_due` subscriptions count;
      trials, pauses and metered usage do not. Amounts are major units, rates
      are percentages (105.25 means 105.25%), and currencies are never summed.
  - name: Credit notes
    description: >-
      A credit note corrects an issued invoice without changing it: "of this
      invoice, this much is no longer owed". Each one has its own number in an
      unbroken sequence (`{prefix}-CN-0001`) and never changes once issued. On
      an unpaid invoice the credit comes off `amountDue`; on a paid one it goes
      back to the customer, refunded to the card, added to the customer balance
      or settled outside Bouclay. What the customer balance paid of an invoice
      always goes back to the balance first. An invoice always satisfies
      `amountDue = total - amountPaid - amountCredited - balanceApplied`. Every
      refund also issues a credit note.
  - name: Payments
  - name: Refunds
    description: >-
      A refund goes back through the gateway that took the payment. Refund part
      of a payment and it stays `succeeded`; refund all of it and it becomes
      `refunded`.
  - name: Chargebacks
    description: >-
      A chargeback is a payment the customer's bank took back. Record one when
      your payment provider reports it, with the provider's dispute reference,
      then record how the dispute ended. While it is open, Bouclay doesn't
      charge or chase the subscription. A chargeback on a credit top-up takes
      back the unspent credit and its bonus; winning the dispute puts back
      exactly that. Uses the `refunds` scope.
  - name: Events
  - name: Checkout sessions
  - name: Portal sessions
  - name: Webhook endpoints
paths:
  /customers/{customer}/credit-balance:
    parameters:
      - $ref: '#/components/parameters/CustomerId'
    get:
      tags:
        - Credits
      summary: Get a customer's credit balance
      description: |
        What this customer has left to spend, one entry per currency they have
        ever held a balance in. Derived from the same ledger an invoice
        spends, so `available` is what the next invoice in that currency will
        take before charging the rest.

        A new invoice spends the balance after tax and records it as
        `balanceApplied`. Card verification charges and payment-link checkouts
        never spend it. Voiding an invoice gives back what it spent.

        An automatic charge below the minimum (the card gateway's own floor,
        or the merchant's higher minimum) is never sent. The invoice is marked
        paid and its amount becomes debt on the balance: `balance` goes
        negative and `available` stays at 0. The next invoice in that currency
        collects it, with a negative `balanceApplied` that adds to its
        `amountDue`.

        Promotional credit lots that are active, unexpired and not yet spent
        are listed in `grants` and counted in `available`. The next invoice
        spends them before tax (soonest expiry first, then promotional before
        prepaid, then oldest), so they lower its VAT like a discount and show
        as `grantApplied`. A lot only pays invoices in its own currency.

        Prepaid credit taxed when bought (`vatTiming: at_purchase`) is spent
        before tax in the same order, but only on lines taxed the way its
        top-up was (the same tax category and rate, in the lot's `tax`): an
        exempt, zero-rated or export line, or one after a rate change, is
        billed normally. A lot bought at a price with VAT included is shown
        in that price's terms in `amount`, `remaining` and `available`, so
        10,000 bought with VAT inside reads 10,000 and covers 10,000 of usage
        priced the same way.

        Prepaid credit taxed when used (`vatTiming: at_redemption`) is spent
        after tax, like cash, in `balanceApplied`: lots expiring soonest
        first, then those that never expire, then the money balance last. A
        top-up awaiting payment shows in `pending`, never in `available`.

        `unbilled` counts usage so far in the customer's open periods
        against the credit: what the invoices at their period ends would
        draw if the usage stopped now, priced the way those invoices will
        be. `projectedAvailable` is what would be left, the number to compare
        with zero before serving a request, and `projectedShortfall` what
        those invoices would still collect. Usage is only as fresh as what
        you have sent, and stopping at zero is your call.

        `held` is what open holds would take, priced on top of that usage
        (`POST /customers/{id}/credit-holds`), and comes off
        `projectedAvailable` too. To stop a customer exactly when their
        credit runs out, reserve before each request instead of reading
        this: a hold is refused when the credit can't cover it.

        Needs the `credits:read` scope on a restricted key.
      operationId: getCustomerCreditBalance
      responses:
        '200':
          $ref: '#/components/responses/CreditBalanceList'
        '404':
          $ref: '#/components/responses/Error'
components:
  parameters:
    CustomerId:
      name: customer
      in: path
      required: true
      schema:
        type: string
      example: ctm_123
  responses:
    CreditBalanceList:
      description: Credit balance list response.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ListEnvelope'
              - type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CreditBalance'
    Error:
      description: Structured API error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    ListEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            type: object
        meta:
          allOf:
            - $ref: '#/components/schemas/Meta'
            - type: object
              properties:
                pagination:
                  $ref: '#/components/schemas/Pagination'
    CreditBalance:
      type: object
      required:
        - object
        - currency
        - available
        - balance
      properties:
        object:
          type: string
          const: credit_balance
        currency:
          type: string
          example: NGN
        available:
          type: number
          minimum: 0
          description: >-
            What the next invoice in this currency can spend, in major units.
            Never below zero.
        pending:
          type: number
          minimum: 0
          description: >-
            Prepaid credit whose top-up invoice is not paid yet, in major units.
            Not part of `available`.
        balance:
          type: number
          description: >-
            The money balance alone, in major units. Negative when the customer
            owes it.
        unbilled:
          type: number
          minimum: 0
          description: >-
            What usage so far in the customer's open periods would draw from
            `available`, in major units, priced as those periods' invoices will
            be.
        held:
          type: number
          minimum: 0
          description: >-
            What open holds would take from `available` on top of `unbilled`, in
            major units, priced the same way.
        projectedAvailable:
          type: number
          minimum: 0
          description: >-
            `available` less `unbilled` and `held`, never below zero: what would
            be left if usage stopped now and every open hold were committed in
            full.
        projectedShortfall:
          type: number
          minimum: 0
          description: >-
            What the invoices for that usage would still collect beyond the
            credit, in major units.
        grants:
          type: array
          description: >-
            Lots of granted credit in this currency that are active (and have
            something left) or awaiting payment. Spent, expired and cancelled
            lots are listed by `GET /credit-grants`.
          items:
            $ref: '#/components/schemas/CreditGrant'
    ErrorResponse:
      type: object
      required:
        - error
        - meta
      properties:
        error:
          type: object
          required:
            - type
            - code
            - detail
          properties:
            type:
              type: string
              enum:
                - request_error
                - api_error
              description: >-
                `api_error` with code `internal_error` (HTTP 500) means the
                fault is ours: retry with the same Idempotency-Key.
              example: request_error
            code:
              type: string
              enum:
                - authentication_failed
                - permission_denied
                - invalid_field
                - invalid_cursor
                - idempotency_conflict
                - meter_not_found
                - meter_archived
                - customer_not_found
                - timestamp_in_future
                - usage_period_closed
                - subscription_paused
                - missing_property
                - invalid_value
                - conflict
                - subscription_suspended
                - no_upcoming_invoice
                - not_found
                - request_error
                - internal_error
            detail:
              type: string
            errors:
              type: array
              items:
                type: object
                required:
                  - field
                  - message
                properties:
                  field:
                    type: string
                  message:
                    type: string
        meta:
          $ref: '#/components/schemas/Meta'
    Meta:
      type: object
      required:
        - requestId
      properties:
        requestId:
          type: string
          example: req_550e8400-e29b-41d4-a716-446655440000
    Pagination:
      type: object
      required:
        - hasMore
      properties:
        hasMore:
          type: boolean
        previous:
          type: string
        next:
          type: string
    CreditGrant:
      type: object
      description: >-
        A lot of granted credit. What is left on it is the sum of its movements,
        so `remaining` is what the next invoice can spend.
      properties:
        id:
          type: string
          example: cg_123
        object:
          type: string
          const: credit_grant
        customer:
          type: object
          properties:
            id:
              type: string
        kind:
          type: string
          enum:
            - promotional
            - prepaid
          description: >-
            Promotional credit, and prepaid credit taxed when bought, are spent
            before tax, like a discount. Prepaid credit taxed when used is spent
            after tax, like cash.
        status:
          type: string
          enum:
            - pending_payment
            - active
            - cancelled
        vatTiming:
          type:
            - string
            - 'null'
          enum:
            - at_purchase
            - at_redemption
            - null
          description: Prepaid credit only. Null on promotional credit.
        tax:
          type:
            - object
            - 'null'
          description: >-
            Prepaid credit taxed when bought only: the top-up's tax. The lot
            only pays invoice lines with this tax category and rate.
          properties:
            category:
              type: string
              enum:
                - standard
                - zero_rated
                - exempt
            rate:
              type:
                - number
                - 'null'
              description: A percentage (7.5); null when the top-up carried no VAT.
              example: 7.5
            inclusive:
              type: boolean
              description: >-
                The top-up's price included VAT, so `amount` and `remaining`
                read in that price's terms.
        denomination:
          type: object
          properties:
            kind:
              type: string
              enum:
                - money
                - unit
            code:
              type: string
              example: NGN
        currency:
          type:
            - string
            - 'null'
          description: The currency of a money lot; it only pays invoices in this currency.
          example: NGN
        amount:
          type: number
          description: >-
            What was granted, in major units. Prepaid credit taxed when bought
            holds what was bought before VAT, and reads in the top-up price's
            terms when that price included VAT.
          example: 2000
        remaining:
          type: number
          description: >-
            What is left to spend, in major units, read like `amount`. 0 once
            the lot has expired: what was left then shows as an `expiry`
            transaction, dated when it expired.
          example: 1500
        expiresAt:
          type:
            - string
            - 'null'
          format: date-time
        expired:
          type: boolean
        reason:
          type: string
        invoice:
          type:
            - object
            - 'null'
          description: The top-up invoice, for bought credit.
          properties:
            id:
              type: string
            number:
              type: string
        customData:
          type:
            - object
            - 'null'
        activatedAt:
          type:
            - string
            - 'null'
          format: date-time
        createdAt:
          type: string
          format: date-time
        transactions:
          type: array
          description: Only on `GET /credit-grants/{id}` and on create.
          items:
            type: object
            properties:
              type:
                type: string
                enum:
                  - grant
                  - invoice_applied
                  - invoice_void
                  - credit_note
                  - expiry
                  - forfeit
                  - refund
                  - adjustment
                description: >-
                  `credit_note` gives back what a paid invoice spent of the lot
                  when a credit note is issued on it.
              amount:
                type: number
                description: Signed major units; negative when spent.
              invoice:
                type:
                  - object
                  - 'null'
                properties:
                  id:
                    type: string
                  number:
                    type: string
              invoiceLineId:
                type:
                  - integer
                  - 'null'
              creditNote:
                type:
                  - object
                  - 'null'
                description: >-
                  The credit note behind a `credit_note`, `refund` or refund
                  `forfeit` row.
                properties:
                  id:
                    type: string
                  number:
                    type: string
              reason:
                type: string
              createdAt:
                type: string
                format: date-time
  securitySchemes:
    SecretKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: '{sk|rk}_{mode}_{random}'
      description: >
        Use a Bouclay secret key (`sk_test_...`, `sk_live_...`) or restricted
        key

        (`rk_test_...`, `rk_live_...`).


        A secret key can call every endpoint. A restricted key can only call the

        endpoints its scopes cover. A scope is `{resource}:{access}`, for
        example

        `customers:read` or `refunds:write`, and write includes read. `GET`

        requests need read access and everything else needs write, except

        `POST /subscriptions/{id}/items/{item}/preview`, which only needs

        `subscriptions:read`. A request outside the key's scopes gets `403

        permission_denied` naming the missing scope, and its `Idempotency-Key`

        is not used up.


        | Resource | Endpoints | Access |

        | --- | --- | --- |

        | `customers` | customers, their addresses and payment methods | read,
        write |

        | `entitlements` | `/customers/{id}/entitlements` | read |

        | `credits` | `/customers/{id}/credit-balance`, credit purchases, credit
        grants, auto-reloads, credit holds (committing one records its usage) |
        read, write |

        | `catalog` | products, plans, prices | read, write |

        | `meters` | meters | read, write |

        | `usage` | meter events, `/customers/{id}/usage` | read, write |

        | `discounts` | discounts | read |

        | `subscriptions` | subscriptions | read, write |

        | `invoices` | invoices | read, write |

        | `credit_notes` | credit notes, `POST /invoices/{id}/credit-notes` (a
        refund to the card also needs `refunds:write`) | read, write |

        | `payments` | payments | read |

        | `refunds` | refunds, `POST /payments/{id}/refunds`, chargebacks and
        their outcome | read, write |

        | `checkout_sessions` | checkout sessions | read, write |

        | `portal_sessions` | portal sessions | write |

        | `events` | events | read |

        | `webhook_endpoints` | webhook endpoints | read, write |

        | `metrics` | metrics | read |


        Publishable keys (`pk_...`) are rejected by server-side API endpoints. A

        key's mode scopes what it can see and charge: a test key can never reach

        a live card.

````

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