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

# Issue a credit note

> Credits an amount spread over the invoice's lines in proportion to what is left on each, or named amounts per line (`lines`, using the invoice lines' `id`). Amounts are major units off the line totals, tax included; the tax inside is reversed at the rate the invoice was issued with. Refused on a void or draft invoice, past the invoice's total, past what is left on a line, and while a charge on the invoice is still processing. An unpaid invoice credited down to nothing is marked `paid` with no payment, and if it was a subscription's overdue debt, the subscription is treated as after a write-off. On a paid invoice `destination` is required: `refund` sends the money back to the card that paid (it needs `refunds:write` too), `balance` keeps it as customer balance that pays their next invoices, and `outside` records that you paid it back another way. A credit on an invoice the customer balance paid part of gives that part back to the balance first (lowering `balanceApplied`), and only the rest comes off `amountPaid`, so a refund only returns what the card paid. When the balance's part covers the whole credit, a `refund` is issued as `balance` and nothing goes to the card.



## OpenAPI

````yaml /api/openapi.yaml post /invoices/{invoice}/credit-notes
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:
  /invoices/{invoice}/credit-notes:
    parameters:
      - $ref: '#/components/parameters/InvoiceId'
    post:
      tags:
        - Credit notes
      summary: Issue a credit note
      description: >-
        Credits an amount spread over the invoice's lines in proportion to what
        is left on each, or named amounts per line (`lines`, using the invoice
        lines' `id`). Amounts are major units off the line totals, tax included;
        the tax inside is reversed at the rate the invoice was issued with.
        Refused on a void or draft invoice, past the invoice's total, past what
        is left on a line, and while a charge on the invoice is still
        processing. An unpaid invoice credited down to nothing is marked `paid`
        with no payment, and if it was a subscription's overdue debt, the
        subscription is treated as after a write-off. On a paid invoice
        `destination` is required: `refund` sends the money back to the card
        that paid (it needs `refunds:write` too), `balance` keeps it as customer
        balance that pays their next invoices, and `outside` records that you
        paid it back another way. A credit on an invoice the customer balance
        paid part of gives that part back to the balance first (lowering
        `balanceApplied`), and only the rest comes off `amountPaid`, so a refund
        only returns what the card paid. When the balance's part covers the
        whole credit, a `refund` is issued as `balance` and nothing goes to the
        card.
      operationId: createCreditNote
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateCreditNoteRequest'
      responses:
        '201':
          $ref: '#/components/responses/CreditNote'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
        '422':
          $ref: '#/components/responses/Error'
components:
  parameters:
    InvoiceId:
      name: invoice
      in: path
      required: true
      schema:
        type: string
      example: inv_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:
    CreateCreditNoteRequest:
      type: object
      required:
        - reason
      description: Give `amount` or `lines`, not both.
      properties:
        reason:
          type: string
          maxLength: 255
          example: Billed 10 seats instead of 8
        destination:
          type: string
          enum:
            - invoice
            - refund
            - balance
            - outside
          description: >-
            `invoice` (the default on an unpaid invoice) takes the credit off
            what is owed. On a paid invoice say `refund`, `balance` or
            `outside`.
        amount:
          $ref: '#/components/schemas/MajorAmount'
        lines:
          type: array
          minItems: 1
          items:
            type: object
            required:
              - invoiceLineId
              - amount
            properties:
              invoiceLineId:
                type: integer
              amount:
                $ref: '#/components/schemas/MajorAmount'
    MajorAmount:
      type:
        - number
        - 'null'
      minimum: 0
      description: Major currency units. For example, 15000 means NGN 15,000.
    ResourceEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
        meta:
          $ref: '#/components/schemas/Meta'
    CreditNote:
      type: object
      properties:
        id:
          type: string
          example: cn_123
        number:
          type: string
          example: 7F3KQ2XA-CN-0001
        invoice:
          type: object
          properties:
            id:
              type: string
            number:
              type:
                - string
                - 'null'
        customer:
          type: object
          properties:
            id:
              type: string
        reason:
          type: string
        destination:
          type: string
          enum:
            - invoice
            - refund
            - balance
            - outside
        refund:
          description: >-
            The refund that sent the money back to the card, when `destination`
            is `refund`.
          type:
            - object
            - 'null'
          properties:
            id:
              type: string
        currency:
          type: string
        subtotal:
          $ref: '#/components/schemas/MajorAmount'
        taxTotal:
          $ref: '#/components/schemas/MajorAmount'
        taxes:
          type: array
          items:
            $ref: '#/components/schemas/InvoiceTax'
        total:
          $ref: '#/components/schemas/MajorAmount'
        sellerTaxId:
          type:
            - string
            - 'null'
        hostedUrl:
          type: string
          format: uri
        creditNotePdfUrl:
          type: string
          format: uri
        issuedAt:
          type: string
          format: date-time
        createdAt:
          type:
            - string
            - 'null'
          format: date-time
        lines:
          type: array
          items:
            type: object
            properties:
              invoiceLineId:
                type: integer
              description:
                type: string
              amount:
                $ref: '#/components/schemas/MajorAmount'
              taxAmount:
                $ref: '#/components/schemas/MajorAmount'
              taxRate:
                type:
                  - number
                  - 'null'
              taxName:
                type:
                  - string
                  - 'null'
              taxReason:
                type:
                  - string
                  - 'null'
              taxInclusive:
                type: boolean
              revenueCode:
                type:
                  - string
                  - 'null'
    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
    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'
  responses:
    CreditNote:
      description: Credit note response.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/ResourceEnvelope'
              - type: object
                properties:
                  data:
                    $ref: '#/components/schemas/CreditNote'
    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.