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

# Sell prepaid credit

> Sells a customer prepaid credit: a credit pack (`priceId`, a one-time
price on a product with `kind: credit`) or a custom `amount`. It
issues a top-up invoice (`billingReason: credit_purchase`) and the
lots it pays for, `pending_payment` until the invoice is paid. Manual
collection (the default) returns the invoice with its `hostedUrl`;
automatic collection charges `paymentMethod`, or the customer's
default card. A voided top-up cancels its lots.

When VAT is charged comes from `vatTiming`, else the pack's product,
else your team default; a purchase that resolves to none is refused.

- `at_redemption`: the top-up carries no VAT and is numbered in its
  own `-PP-` sequence. The credit is spent after tax, like cash, and
  VAT is charged on what it pays for.
- `at_purchase`: the top-up is a normal tax invoice in your invoice
  sequence, taxed by the pack's product's tax category (or
  `taxCategory` for a custom amount) with VAT included or added on
  top as your prices are. The lot holds what was bought before VAT
  and is spent before tax, only on lines with the same tax category
  and rate, so the usage it covers isn't taxed again.

Expiry comes from `expiresAfterMonths` (send `null` for never), else
the pack's product; it counts from payment. A bonus pack, or
`creditAmount` above `amount`, gives the extra as its own promotional
lot with no VAT, spent first and forfeited if the top-up is refunded
or charged back. A refund is capped at the credit not yet spent; for
`at_purchase` that is the unspent credit plus its VAT, which the
credit note reverses at the top-up's rate.

Needs `credits:write` on a restricted key. Emits
`credit_grant.created` for each lot, then `credit_grant.updated`
when the invoice is paid.




## OpenAPI

````yaml /api/openapi.yaml post /customers/{customer}/credit-purchases
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-purchases:
    parameters:
      - $ref: '#/components/parameters/CustomerId'
    post:
      tags:
        - Credits
      summary: Sell prepaid credit
      description: |
        Sells a customer prepaid credit: a credit pack (`priceId`, a one-time
        price on a product with `kind: credit`) or a custom `amount`. It
        issues a top-up invoice (`billingReason: credit_purchase`) and the
        lots it pays for, `pending_payment` until the invoice is paid. Manual
        collection (the default) returns the invoice with its `hostedUrl`;
        automatic collection charges `paymentMethod`, or the customer's
        default card. A voided top-up cancels its lots.

        When VAT is charged comes from `vatTiming`, else the pack's product,
        else your team default; a purchase that resolves to none is refused.

        - `at_redemption`: the top-up carries no VAT and is numbered in its
          own `-PP-` sequence. The credit is spent after tax, like cash, and
          VAT is charged on what it pays for.
        - `at_purchase`: the top-up is a normal tax invoice in your invoice
          sequence, taxed by the pack's product's tax category (or
          `taxCategory` for a custom amount) with VAT included or added on
          top as your prices are. The lot holds what was bought before VAT
          and is spent before tax, only on lines with the same tax category
          and rate, so the usage it covers isn't taxed again.

        Expiry comes from `expiresAfterMonths` (send `null` for never), else
        the pack's product; it counts from payment. A bonus pack, or
        `creditAmount` above `amount`, gives the extra as its own promotional
        lot with no VAT, spent first and forfeited if the top-up is refunded
        or charged back. A refund is capped at the credit not yet spent; for
        `at_purchase` that is the unspent credit plus its VAT, which the
        credit note reverses at the top-up's rate.

        Needs `credits:write` on a restricted key. Emits
        `credit_grant.created` for each lot, then `credit_grant.updated`
        when the invoice is paid.
      operationId: purchaseCredit
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                priceId:
                  type: string
                  description: >-
                    A credit pack. Leave out `amount`, `currency` and
                    `creditAmount`.
                  example: price_123
                amount:
                  type: number
                  exclusiveMinimum: 0
                  description: >-
                    What the customer pays, in major units of `currency` (before
                    VAT added on top, for `at_purchase` when your prices exclude
                    VAT). At least the minimum charge for the currency.
                  example: 10000
                currency:
                  type: string
                  description: Defaults to the customer's.
                  example: NGN
                creditAmount:
                  type: number
                  exclusiveMinimum: 0
                  description: >-
                    The credit received, when more than `amount`. The difference
                    is a bonus.
                vatTiming:
                  type: string
                  enum:
                    - at_redemption
                    - at_purchase
                  description: >-
                    `at_redemption`: no VAT on the top-up, VAT on what the
                    credit pays for. `at_purchase`: VAT on the top-up, and the
                    credit is spent before tax.
                taxCategory:
                  type: string
                  enum:
                    - standard
                    - zero_rated
                    - exempt
                  description: >-
                    A custom amount's tax category, which taxes an `at_purchase`
                    top-up and decides the lines its credit may pay. Defaults to
                    `standard`. A pack uses its product's; leave this out with
                    `priceId`.
                revenueCode:
                  type: string
                  maxLength: 64
                  description: >-
                    A custom amount's revenue code, kept on the top-up line.
                    Leave out with `priceId`.
                expiresAfterMonths:
                  type:
                    - integer
                    - 'null'
                  minimum: 1
                  maximum: 120
                collectionMode:
                  type: string
                  enum:
                    - manual
                    - automatic
                  default: manual
                paymentMethod:
                  type: string
                  description: A saved card for automatic collection.
                customData:
                  type:
                    - object
                    - 'null'
                  description: Kept on the prepaid lot.
      responses:
        '201':
          description: The top-up invoice and its lots.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      object:
                        type: string
                        const: credit_purchase
                      invoice:
                        $ref: '#/components/schemas/Invoice'
                      grants:
                        type: array
                        description: The prepaid lot, then the bonus lot if any.
                        items:
                          $ref: '#/components/schemas/CreditGrant'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/Error'
components:
  parameters:
    CustomerId:
      name: customer
      in: path
      required: true
      schema:
        type: string
      example: ctm_123
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 255
      description: >-
        Required for POST and PATCH requests. Replays identical method + path +
        body requests and rejects mismatched re-use.
  schemas:
    Invoice:
      type: object
      properties:
        id:
          type: string
        number:
          type: string
        status:
          type: string
        billingReason:
          type: string
          description: >-
            `subscription_create`, `subscription_cycle`, `subscription_update`,
            `subscription_threshold`, `manual`, or `credit_purchase` (a top-up
            of prepaid credit: taxed when used, numbered `{prefix}-PP-0001` with
            no VAT on it; taxed when bought, a normal tax invoice in your
            invoice sequence).
        purpose:
          type:
            - string
            - 'null'
          enum:
            - card_verification
            - null
          description: >-
            `card_verification` for the small charge that saves a card (a
            checkout session, a card update, a card required at signup). It is
            refunded once it settles and is no sale, so leave it out of the
            customer's receipts.
        collectionMode:
          type: string
          enum:
            - automatic
            - manual
        currency:
          type: string
        subtotal:
          $ref: '#/components/schemas/MajorAmount'
        grantApplied:
          $ref: '#/components/schemas/MajorAmount'
          description: >-
            Credit spent before tax when the invoice was created (promotional,
            and prepaid credit taxed when bought), so it lowered the VAT like a
            discount: `total = subtotal - discountTotal - grantApplied + tax
            added on top`. Never changes; voiding the invoice gives the credit
            back to its lots.
        taxTotal:
          $ref: '#/components/schemas/MajorAmount'
          description: >-
            All tax on the invoice, added on top or included. The total includes
            it either way; the invoice's net is `total - taxTotal`.
        taxes:
          type: array
          description: >-
            The tax broken down by rate. Empty when the business doesn't charge
            tax.
          items:
            $ref: '#/components/schemas/InvoiceTax'
        total:
          $ref: '#/components/schemas/MajorAmount'
        minimumCommitment:
          $ref: '#/components/schemas/MinimumCommitmentCheck'
        spendingCap:
          description: >-
            The spending cap the invoice held its period to, when the cap stops
            billing. Null otherwise (no cap, or one that only warns).
          type:
            - object
            - 'null'
          properties:
            amount:
              type: number
              description: The period's cap, in major units.
            counted:
              type: number
              description: >-
                What the period spent toward it with this invoice, before
                anything was taken back.
            takenBack:
              type: number
              description: What the invoice's `maximum` lines took off.
        sellerTaxId:
          type:
            - string
            - 'null'
          description: The business's TIN when the invoice was issued.
        sellerVatRegistered:
          type: boolean
          description: >-
            Whether the business was VAT registered when the invoice was issued.
            Such an invoice is titled "Tax invoice". Never changes after issue.
        billingAddress:
          type:
            - object
            - 'null'
          description: >-
            The customer's billing address when the invoice was issued, as
            printed on it (name, line1, line2, city, region, postalCode,
            country, phone, singleLine). Null when the customer had none. Never
            changes after issue; a later address goes on later invoices.
        reference:
          type:
            - string
            - 'null'
          description: >-
            The buyer's purchase order number or reference it was issued with:
            its own, or its subscription's at the time. Never changes after
            issue.
        amountPaid:
          $ref: '#/components/schemas/MajorAmount'
          description: >-
            What the customer paid and the invoice kept: a credit note that
            refunds the card, goes to the customer balance or is settled outside
            Bouclay lowers it, after what `balanceApplied` holds.
        amountCredited:
          $ref: '#/components/schemas/MajorAmount'
          description: >-
            What credit notes took off. `amountDue = total - amountPaid -
            amountCredited - balanceApplied`; `total` never changes.
        balanceApplied:
          type: number
          description: >-
            What the customer's prepaid credit taxed when used and money balance
            paid, after tax, when the invoice was created, in major units. Back
            to 0 if the invoice is voided, which returns it to the balance.
            Negative when the invoice collects a debt carried from an earlier
            invoice that was below the minimum charge: it then adds to
            `amountDue`. An invoice that was itself carried shows its amount
            here, paid from the balance as debt.
          example: 0
        amountDue:
          $ref: '#/components/schemas/MajorAmount'
        paidAt:
          type:
            - string
            - 'null'
          format: date-time
        customer:
          $ref: '#/components/schemas/NestedCustomer'
        subscription:
          type:
            - object
            - 'null'
          properties:
            id:
              type: string
            status:
              type: string
            pauseReason:
              type:
                - string
                - 'null'
              enum:
                - requested
                - payment_overdue
                - plan_retired
                - null
        payment:
          description: >-
            The payment that settled the invoice, else its latest attempt. Null
            when nothing has been attempted. Refund through its `id`.
          oneOf:
            - $ref: '#/components/schemas/Payment'
            - type: 'null'
        dueAt:
          type:
            - string
            - 'null'
          format: date-time
        hostedUrl:
          type:
            - string
            - 'null'
          format: uri
          description: >-
            The page where the customer sees and pays the invoice. Null for
            drafts.
        invoicePdfUrl:
          type:
            - string
            - 'null'
          format: uri
          description: The invoice as a PDF. Null for drafts.
        createdAt:
          type:
            - string
            - 'null'
          format: date-time
        lines:
          type: array
          items:
            type: object
            properties:
              id:
                type: integer
                description: Names this line in a credit note's `lines`.
              description:
                type: string
              kind:
                type: string
                description: >-
                  What the line bills. `minimum` is what the period fell short
                  of the subscription's minimum commitment: no product, no
                  discount, one line per tax category, taxed like what it tops
                  up.
                enum:
                  - plan
                  - addon
                  - usage
                  - proration
                  - one_time
                  - tax
                  - discount
                  - credit
                  - credit_purchase
                  - minimum
                  - maximum
              quantity:
                type: integer
              unitAmount:
                $ref: '#/components/schemas/MajorAmount'
                description: >-
                  The price of one unit, so `quantity × unitAmount` is the line
                  before discount. Null when no one unit price makes the amount:
                  a tiered seat price, whose `tierBreakdown` says how it adds
                  up.
              quantityLabel:
                description: >-
                  The quantity in the charge's own words when the charge names
                  its unit, such as "3 learners", as it was when the invoice was
                  issued. Null otherwise, and on usage and proration lines.
                type:
                  - string
                  - 'null'
              tierBreakdown:
                description: >-
                  How a line on a graduated or volume seat price adds up, such
                  as "1 × NGN 12,000.00 + 2 × NGN 10,000.00" for three seats at
                  ₦12,000 for the first and ₦10,000 for each after. Null for
                  every other line.
                type:
                  - string
                  - 'null'
              total:
                $ref: '#/components/schemas/MajorAmount'
              billingTiming:
                description: >-
                  `arrears` for a line that pays for the period that just ended
                  (usage, fees billed at the end), `advance` for one paying for
                  the next period. Null on lines outside a period boundary, like
                  one-off charges.
                type:
                  - string
                  - 'null'
                enum:
                  - advance
                  - arrears
                  - null
              periodStart:
                type:
                  - string
                  - 'null'
                format: date-time
              periodEnd:
                type:
                  - string
                  - 'null'
                format: date-time
              usageQuantity:
                description: What a usage line measured.
                type:
                  - integer
                  - 'null'
              taxCategory:
                description: >-
                  The product's tax category when the invoice was issued. Null
                  on a line with no product.
                type:
                  - string
                  - 'null'
                enum:
                  - standard
                  - zero_rated
                  - exempt
                  - null
              revenueCode:
                description: The product's revenue code when the invoice was issued.
                type:
                  - string
                  - 'null'
              discountAmount:
                $ref: '#/components/schemas/MajorAmount'
              grantApplied:
                $ref: '#/components/schemas/MajorAmount'
                description: >-
                  Credit taken off this line before tax, in the line's price
                  terms. Prepaid credit taxed when bought only pays lines with
                  its top-up's tax category and rate.
              taxAmount:
                $ref: '#/components/schemas/MajorAmount'
              taxRate:
                description: >-
                  The rate the line was taxed at, as a percentage, frozen when
                  the invoice was issued. Null when no tax applies (an exempt
                  product or customer, or a business that doesn't charge tax).
                type:
                  - number
                  - 'null'
                example: 7.5
              taxName:
                type:
                  - string
                  - 'null'
                example: VAT
              taxReason:
                type:
                  - string
                  - 'null'
                description: >-
                  Why the rate applies, when it needs saying. For example,
                  `export of services` on a zero-rated export.
                example: export of services
              taxInclusive:
                type: boolean
                description: >-
                  True when `taxAmount` is inside the line's total rather than
                  added on top.
    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
    MajorAmount:
      type:
        - number
        - 'null'
      minimum: 0
      description: Major currency units. For example, 15000 means NGN 15,000.
    InvoiceTax:
      type: object
      description: >-
        One tax rate on an invoice, as it is printed: added on top of the
        subtotal, or included in the prices (VAT-inclusive pricing).
      properties:
        name:
          type: string
          example: VAT
        rate:
          type:
            - number
            - 'null'
          description: The rate as a percentage. Null for tax that came without a rate.
          example: 7.5
        reason:
          type:
            - string
            - 'null'
          description: >-
            Why the rate applies, when it needs saying. For example, `export of
            services` on a zero-rated export, printed as "VAT (0%, export of
            services)".
        inclusive:
          type: boolean
          description: >-
            True when the tax is inside the line amounts rather than added on
            top.
        amount:
          $ref: '#/components/schemas/MajorAmount'
    MinimumCommitmentCheck:
      type:
        - object
        - 'null'
      description: >-
        The minimum commitment the invoice held the period it closes to. Null
        when the subscription had none for the period. On an upcoming invoice,
        what counted so far.
      properties:
        amount:
          type: number
          description: >-
            The period's minimum, in major units, prorated for the part of the
            period it applied (a mid-period change, a pause, a cancellation).
        counted:
          type: number
          description: >-
            What the period billed toward it, this invoice included: fees,
            usage, prorations and credits, after discounts and before tax.
        shortfall:
          type: number
          description: What the invoice's `minimum` lines bill, `max(0, amount - counted)`.
    NestedCustomer:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
          format: email
        name:
          type:
            - string
            - 'null'
        taxId:
          description: >-
            On an invoice, the customer's TIN as it was when the invoice was
            issued.
          type:
            - string
            - 'null'
    Payment:
      type: object
      properties:
        id:
          type: string
        status:
          type: string
          enum:
            - pending
            - processing
            - succeeded
            - failed
            - refunded
          description: >-
            `processing` means the charge's outcome isn't known yet (a gateway
            timeout, or a charge the gateway is still clearing). It may have
            gone through, so it is never retried: Bouclay settles it from the
            gateway's webhook or by looking the charge up, and it becomes
            `succeeded` or `failed`.
        amount:
          $ref: '#/components/schemas/MajorAmount'
        amountRefunded:
          $ref: '#/components/schemas/MajorAmount'
        amountChargedBack:
          $ref: '#/components/schemas/MajorAmount'
          description: >-
            Taken back by chargebacks still open or lost. A dispute you won
            returned its money, so it no longer counts.
        currency:
          type: string
        failureCode:
          type:
            - string
            - 'null'
        failureReason:
          type:
            - string
            - 'null'
        attemptNumber:
          type: integer
        manualMethod:
          description: >-
            How a payment recorded by hand was made (`POST
            /invoices/{id}/payments`); null for a gateway's.
          type:
            - string
            - 'null'
          enum:
            - cash
            - bank_transfer
            - pos
            - null
        reference:
          description: The reference given with a payment recorded by hand.
          type:
            - string
            - 'null'
        processedAt:
          type:
            - string
            - 'null'
          format: date-time
        invoice:
          type: object
          properties:
            id:
              type: string
        customer:
          type: object
          properties:
            id:
              type: string
        createdAt:
          type:
            - string
            - 'null'
          format: date-time
    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
  responses:
    Error:
      description: Structured API error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  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.