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

# Give a subscriber credit

> Puts credit on the subscriber's account with you. It comes off their next invoice, reducing what their card is charged; no money moves out, and no receipt already issued changes. The credit belongs to the CUSTOMER, so if the same buyer has two subscriptions with you in the same currency it goes to whichever invoice falls due first. The amount is positive integer minor units and may cover the whole of the next invoice — at most its amount due. A credit that covers it in full charges the card nothing, and that month is still recorded as a sale of zero with an invoice and a receipt saying the credit covered it. More than the amount due is refused, and the error names the largest amount you can give. The currency is the subscription's; there is no currency field. Only an owner or admin may do this, from a signed-in session: an API key cannot, because the credit must name the person who gave it.

<Note>
  **Webhook:** none. The credit changes no subscription state. A renewal that the credit pays sends `checkout.session.completed` and `subscription.renewed`. See [Which action fires which event](/webhooks/events#which-action-fires-which-event).
</Note>


## OpenAPI

````yaml POST /gateway/subscriptions/{id}/credits
openapi: 3.1.0
info:
  title: AgentaOS Gateway API
  version: 1.0.0
  description: >-
    The AgentaOS REST API: payment links, checkouts, subscriptions, customers,
    invoices, and the unified transactions feed. This is the same API the
    TypeScript SDK (`@agentaos/pay`) and the `agenta` CLI call underneath.


    ## Money model


    - **Decimal currency units (`number`)**: a plain `amount` field (on a create
    body, a Payment Link, a Checkout's `amount_override`, or an Invoice's
    `amount`/`fiat_amount`/`tax_amount`) is in currency units. `49.99` means EUR
    49.99, never cents.

    - **Integer minor units (`number`)**: exactly one field breaks that rule:
    `unitAmountMinor` on a Subscription. It is an integer count of the smallest
    currency unit (`1999` means EUR 19.99), named with a `Minor` suffix
    specifically so it is never confused with a plain `amount`. The `money`
    object on Transactions and the `earnings` object on a retrieved Invoice are
    also integer minor units, computed server-side.

    - **String, on webhooks**: the `amount` inside a webhook payload's `data`
    object is a string (e.g. `"49.99"`), since JSON numbers silently drop
    trailing zeros and this value must round-trip exactly for accounting.


    ## Naming convention


    Most response fields mirror the underlying database column and are
    `snake_case` (`created_at`, `seller_mode`, `buyer_email`, ...). A small
    number of computed convenience fields are camelCase with no snake_case
    equivalent: `checkoutUrl` on Checkouts and Payment Links, `sellerMode` on
    Payment Links specifically, `money` on Transactions, and `earnings` on a
    single retrieved Invoice. Two resources (Subscriptions and Customers) are
    fully camelCase end to end.


    ## Pagination


    Every list endpoint takes `limit` and `offset` and returns `{ items, total,
    hasMore }`. `hasMore` is computed server-side (`offset + items.length <
    total`); never derive it client-side.
  contact:
    name: AgentaOS
    url: https://agentaos.ai
  license:
    name: Proprietary
    url: https://agentaos.ai
servers:
  - url: https://api.agentaos.ai/api/v1
    description: Production
security:
  - ApiKeyAuth: []
tags:
  - name: Payment Links
    description: >-
      Reusable, shareable payment products: a fixed amount and description
      behind one URL.
  - name: Checkouts
    description: >-
      One attempt to collect one payment, standalone or built from a payment
      link. Wire path is `/gateway/sessions`; the SDK and CLI call this resource
      "checkout".
  - name: Subscriptions
    description: >-
      Merchant-side read and management surface for recurring plans. Created
      automatically when a buyer pays a `type: subscription` payment link.
  - name: Customers
    description: 'Read-only surface: everyone who has paid you or been sent an invoice.'
  - name: Invoices
    description: >-
      Every checkout issues an invoice. List, retrieve, void, export, and
      download PDFs/receipts.
  - name: Transactions
    description: >-
      The unified activity feed: every inbound payment and outbound send, across
      every rail, in one list.
  - name: Businesses
    description: >-
      For platforms: the businesses you manage, a client's business or another
      app of your own company. To act as one of them on any other endpoint, send
      its id in the `AgentaOS-Account` header. Every field on this resource is
      camelCase on the wire.
paths:
  /gateway/subscriptions/{id}/credits:
    post:
      tags:
        - Subscriptions
      summary: Give a subscriber credit
      description: >-
        Puts credit on the subscriber's account with you. It comes off their
        next invoice, reducing what their card is charged; no money moves out,
        and no receipt already issued changes. The credit belongs to the
        CUSTOMER, so if the same buyer has two subscriptions with you in the
        same currency it goes to whichever invoice falls due first. The amount
        is positive integer minor units and may cover the whole of the next
        invoice — at most its amount due. A credit that covers it in full
        charges the card nothing, and that month is still recorded as a sale of
        zero with an invoice and a receipt saying the credit covered it. More
        than the amount due is refused, and the error names the largest amount
        you can give. The currency is the subscription's; there is no currency
        field. Only an owner or admin may do this, from a signed-in session: an
        API key cannot, because the credit must name the person who gave it.
      operationId: grantSubscriptionCredit
      parameters:
        - $ref: '#/components/parameters/SubscriptionId'
        - name: Idempotency-Key
          in: header
          required: true
          description: >-
            Required, not optional. Mint one string per credit you mean to give,
            and send the same one again if the call times out and you retry.
            Nothing on our side survives a retry to recognise it by, so a retry
            without a key credits the subscriber a second time. Two different
            keys for the same amount give two credits, which is how you
            deliberately credit someone twice.
          schema:
            type: string
            maxLength: 200
          example: credit-2026-09-14-downtime-acme
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GrantCreditRequest'
            example:
              amountMinor: 500
              reason: Two days of downtime in August
      responses:
        '201':
          description: The credit was given.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubscriptionCredit'
              example:
                id: cbtxn_1NXyZ2eZvKYlo2C0
                amountMinor: 500
                currency: eur
                reason: Two days of downtime in August
                creditBalanceMinor: 500
                nextInvoiceMinor: 1900
                nextInvoiceDueAfterCreditMinor: 1400
                nextInvoiceAt: '2026-10-01T00:00:00.000Z'
                createdBy: 3f1c2d4e-5a6b-4c7d-8e9f-0a1b2c3d4e5f
                createdAt: '2026-09-14T10:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
      security:
        - SessionAuth: []
components:
  parameters:
    SubscriptionId:
      name: id
      in: path
      required: true
      description: >-
        Subscription UUID (the `id` field from the list response, not
        `stripeSubscriptionId`).
      schema:
        type: string
        format: uuid
  schemas:
    GrantCreditRequest:
      type: object
      properties:
        amountMinor:
          type: integer
          minimum: 1
          description: >-
            Positive integer minor units of the subscription's currency, at most
            the next invoice's amount due. There is no currency field: a credit
            in the wrong currency is accepted by the processor and then never
            reaches an invoice.
        reason:
          type: string
          maxLength: 200
          description: >-
            Why you gave it. Shown back to you and on the credit at the
            processor.
      required:
        - amountMinor
        - reason
    SubscriptionCredit:
      type: object
      description: >-
        A credit you gave, with both figures it changes. Money is integer minor
        units.
      properties:
        id:
          type: string
          description: The credit's id at the payment processor. There is no id of ours.
        amountMinor:
          type: integer
          description: What you gave, positive.
        currency:
          type: string
          description: The subscription's, lower-case.
        reason:
          type: string
        creditBalanceMinor:
          type: integer
          description: All unspent credit on this customer's account with you.
        nextInvoiceMinor:
          type: integer
          description: What their next invoice would have taken before this credit.
        nextInvoiceDueAfterCreditMinor:
          type: integer
          description: What it will take now.
        nextInvoiceAt:
          type: string
          format: date-time
        createdBy:
          type: string
          format: uuid
          description: The person who gave it.
        createdAt:
          type: string
          format: date-time
      required:
        - id
        - amountMinor
        - currency
        - reason
        - creditBalanceMinor
        - nextInvoiceMinor
        - nextInvoiceDueAfterCreditMinor
        - nextInvoiceAt
        - createdBy
        - createdAt
    Error:
      type: object
      description: >-
        Every non-2xx response is JSON in this shape. Every response (success or
        error) also carries an `x-request-id` response header; send that header
        on your request to have your own id echoed back, otherwise the server
        mints one. On errors the same id is repeated in the `requestId` body
        field.
      properties:
        statusCode:
          type: integer
          description: Same as the HTTP status code.
        code:
          type: string
          description: >-
            Present on some refusals: a stable machine-readable reason, e.g.
            `live_key_required`.
        message:
          description: >-
            Human-readable. A validation failure (400) returns an array of
            per-field messages instead of one string.
          anyOf:
            - type: string
            - type: array
              items:
                type: string
        errors:
          type: array
          description: >-
            Present on 400 validation failures: one entry per invalid field. The
            flat human-readable strings stay in `message` for back-compat; this
            is the machine-readable, per-field view.
          items:
            type: object
            properties:
              field:
                type: string
                description: >-
                  Name of the invalid field, dot-notated for nested properties
                  (e.g. `checkoutFields.0.type`).
              message:
                type: string
                description: Validation messages for that field, joined with `, `.
            required:
              - field
              - message
        requestId:
          type: string
          description: >-
            Correlation id, identical to the `x-request-id` response header.
            Include it when contacting support. On success it is available on
            the header only, not the body.
        timestamp:
          type: string
          format: date-time
      required:
        - statusCode
        - message
        - timestamp
  responses:
    BadRequest:
      description: >-
        A required field is missing, out of range, or the wrong type.
        `ValidationPipe` also rejects any field not in the documented schema.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 400
            message: amount is required when creating a session without a link
            timestamp: '2026-08-06T12:00:00.000Z'
    Unauthorized:
      description: Missing or invalid `x-api-key`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 401
            message: Invalid API key
            timestamp: '2026-08-06T12:00:00.000Z'
    Forbidden:
      description: The key is valid but the resource belongs to a different organization.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 403
            message: Payment link belongs to another organization
            timestamp: '2026-08-06T12:00:00.000Z'
    NotFound:
      description: >-
        Resource not found (or not visible to your key; the two are not
        distinguished).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            statusCode: 404
            message: Payment link not found
            timestamp: '2026-08-06T12:00:00.000Z'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Your secret key, from app.agentaos.ai -> Settings -> Developers -> API
        Keys. The key's prefix is both its identity and its environment:
        `sk_test_...` (sandbox, free, no verification) or `sk_live_...` (real
        money, requires business verification). A missing or invalid key returns
        401.

````