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

# Reserve credits

> Prices work before it happens and, optionally, reserves credit for it. The request carries no credit amount: describe the work as the events it will produce, and the response's `charges` say what it prices to per metric.

Send `events` alone to get a price with nothing recorded. Send `reserve` to open a hold, then close it with `POST /beta/credits/track`. `"reserve": {}` with `events` holds enough for exactly that work; `"reserve": {"max_quantity": "100000"}` with `event_name` holds a ceiling in metric units. An `Idempotency-Key` is required whenever `reserve` is set. Each metric is held at the rate frozen when the hold opened, and the hold expires after 15 minutes unless `ttl_seconds` asks for longer (maximum 4 hours).

Only credit-gated metrics are accepted: an event feeding no credit-consuming metric is rejected with `422` and belongs on `POST /beta/events`. Insufficient credit returns `200` with `allowed: false`; an `allowed: true` response carrying no `reservation_id` opened no hold. Retry timeouts and `5xx` with the same key. `409` means the key is bound to a different reservation. Back off on `429`, which also covers a ledger at its concurrent-hold cap.



## OpenAPI

````yaml /api-reference/v2/specs/2026-04-01.json post /credits/check
openapi: 3.1.0
info:
  title: Alguna Public API
  version: '2026-04-01'
servers:
  - url: https://api.alguna.io
security:
  - bearerAuth: []
tags:
  - name: Billing Events and Metrics
  - name: Checkout Sessions
  - name: Credit Notes
  - name: Credits
  - name: Customer Portal Sessions
  - name: Customers
  - name: Insights
  - name: Integrations
  - name: Invoices
  - name: Payments
  - name: Plans
  - name: Product Bundles
  - name: Products
  - name: Refunds
  - name: Revenue Schedules
  - name: Subscription Changes
  - name: Subscription Versions
  - name: Subscriptions
  - name: Tax
  - name: Wallet Grants
  - name: Wallets
paths:
  /credits/check:
    post:
      tags:
        - Credits
      summary: Reserve credits
      description: >-
        Prices work before it happens and, optionally, reserves credit for it.
        The request carries no credit amount: describe the work as the events it
        will produce, and the response's `charges` say what it prices to per
        metric.


        Send `events` alone to get a price with nothing recorded. Send `reserve`
        to open a hold, then close it with `POST /beta/credits/track`.
        `"reserve": {}` with `events` holds enough for exactly that work;
        `"reserve": {"max_quantity": "100000"}` with `event_name` holds a
        ceiling in metric units. An `Idempotency-Key` is required whenever
        `reserve` is set. Each metric is held at the rate frozen when the hold
        opened, and the hold expires after 15 minutes unless `ttl_seconds` asks
        for longer (maximum 4 hours).


        Only credit-gated metrics are accepted: an event feeding no
        credit-consuming metric is rejected with `422` and belongs on `POST
        /beta/events`. Insufficient credit returns `200` with `allowed: false`;
        an `allowed: true` response carrying no `reservation_id` opened no hold.
        Retry timeouts and `5xx` with the same key. `409` means the key is bound
        to a different reservation. Back off on `429`, which also covers a
        ledger at its concurrent-hold cap.
      operationId: check-credits
      parameters:
        - in: header
          name: Alguna-Version
          required: true
          schema:
            enum:
              - '2026-04-01'
            type: string
        - in: header
          name: Idempotency-Key
          schema:
            description: >-
              A unique string used to ensure the request is processed exactly
              once. If you retry a request with the same idempotency key within
              24 hours, the original response is returned without re-executing
              the operation.
            example: ik_a1b2c3d4e5f6
            maxLength: 255
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CheckCreditsRequest'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CheckCreditsResponse'
          description: Success
          headers:
            Idempotency-Key:
              description: Echo of the idempotency key provided in the request
              schema:
                type: string
            Idempotent-Replayed:
              description: >-
                Whether this response was replayed from a previous request
                (true) or freshly executed (false)
              schema:
                type: boolean
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Bad Request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Unauthorized
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Not Found
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: >-
            Conflict — a request with this idempotency key is currently being
            processed
        '422':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Unprocessable Entity
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          description: Internal Server Error
components:
  schemas:
    CheckCreditsRequest:
      properties:
        account:
          description: Customer account ID or alias the credits belong to
          example: cust_abc123
          type: string
        event_name:
          description: >-
            The usage event the work will produce. This is what resolves which
            billable metrics the check is against — you name what you are about
            to do, not which metric counts it. An event that feeds no
            credit-consuming metric is rejected with 422 — that usage belongs on
            POST /beta/events; an event feeding several credit-consuming metrics
            is checked (and held) against all of them at once. Optional when
            events carries the sample; required for reserve.max_quantity.
          example: api_call
          type: string
        events:
          description: >-
            A sample of the work about to happen. The response's charges carry
            what that work would cost per metric, priced exactly as /track would
            price it. Nothing is recorded and no credit moves — this is how you
            learn a price without performing the work. With reserve: {}, the
            hold is sized to exactly these events.
          items:
            $ref: '#/components/schemas/TrackCreditEvent'
          type: array
        properties:
          description: >-
            Event properties used to resolve customer-specific filter groups for
            this check. Not a billing input: this call records nothing.
          type: object
        reserve:
          allOf:
            - $ref: '#/components/schemas/ReserveCreditsRequest'
          description: >-
            Open a durable hold alongside the check. An object, never a number:
            send {} to hold enough for exactly the events in this request, or
            {"max_quantity": "…"} with event_name for an open-ended ceiling in
            metric units. The old numeric credit-amount form is rejected — the
            request no longer carries credit amounts anywhere. Omit for a
            cost-free preflight or quote (no hold is opened). Requires an
            Idempotency-Key.
          nullable: true
        ttl_seconds:
          description: >-
            How long the hold stays open, in seconds. Defaults to 900 (15
            minutes) and cannot exceed 14400 (4 hours), the maximum lifetime any
            hold may reach. Use it for a run that cannot report progress as it
            goes; a run that can should send partial settlements to /track
            instead, since each one slides this same TTL forward. Requires
            reserve. Not part of idempotency identity: a retry under the same
            Idempotency-Key replays the original hold with its original
            expires_at, whatever ttl_seconds it now carries.
          example: 3600
          format: int64
          maximum: 14400
          minimum: 1
          nullable: true
          type: integer
      required:
        - account
      type: object
    CheckCreditsResponse:
      properties:
        allowed:
          description: >-
            Whether the named work is covered by available credit (and, with
            reserve, whether a hold was opened).
          type: boolean
        at_zero_action:
          description: >-
            The at-zero policy the caller should apply on a deny: stop (hard
            deny) or overflow (fall through to metered billing). When the work
            feeds several metrics with differing policies, the strictest (stop)
            is reported.
          example: overflow
          type: string
        balances:
          description: >-
            Per-metric, per-ledger availability — one entry per ledger, so a
            single-ledger account returns one. Always an array, but not always a
            populated one: it is empty when no availability was resolved to
            report, which is every response the gate could not read balances for
            (a back-pressure fail-open, a failed post-hold refresh, a failed
            report behind a deny) and every response served while the
            organization's gate is disabled. Check the length before indexing.
          items:
            $ref: '#/components/schemas/CreditBalance'
          type: array
        charges:
          description: >-
            One entry per metric the named work feeds: what the sampled events
            (or the max_quantity ceiling) price to per metric, and what a hold
            covers of it. Where no hold was opened — a quote, a denied reserve,
            a degraded fail-open — covered is zero and overflow carries the
            whole priced amount as the cash exposure of the described work. A
            check that named work but sampled none prices to zero throughout.
            Always an array, length 1 in the single-metric case.
          items:
            $ref: '#/components/schemas/CreditCharge'
          type: array
        expires_at:
          description: >-
            When the opened hold expires. Present only when a reservation was
            opened.
          example: '2026-04-01T10:05:00Z'
          format: date-time
          nullable: true
          type: string
        reservation_id:
          description: >-
            Reservation token to settle later via /track. Empty unless a hold
            was opened (reserve).
          example: crt_abc123
          type: string
        totals:
          allOf:
            - $ref: '#/components/schemas/CreditTotals'
          description: >-
            The charges summed. Derived from charges — the breakdown is
            authoritative.
      required:
        - allowed
        - at_zero_action
        - balances
        - charges
        - reservation_id
        - totals
      type: object
    ErrorResponse:
      properties:
        detail:
          type: string
        status:
          format: int64
          type: integer
      required:
        - status
        - detail
      type: object
    TrackCreditEvent:
      properties:
        event_name:
          description: >-
            Usage event name. On a settlement it must be counted by one of the
            metrics the reservation covers, or the request is rejected.
          example: agent_step
          type: string
        properties:
          description: >-
            Event properties. For a metric that sums a property, the aggregation
            field is required here and is the billing input for this event.
          type: object
        timestamp:
          description: When the usage happened (RFC3339). Defaults to receipt time.
          example: '2026-04-01T10:00:00Z'
          format: date-time
          nullable: true
          type: string
        unique_id:
          description: >-
            Stable dedupe identity for this event, max 72 characters. Must be
            unique within the payload; reuse the same identity on every retry.
          example: run_88f_step1
          maxLength: 72
          type: string
      required:
        - event_name
        - unique_id
      type: object
    ReserveCreditsRequest:
      properties:
        max_quantity:
          description: >-
            Ceiling for an open-ended run, in METRIC UNITS (your events' own
            vocabulary), never credits. The hold is sized to max_quantity × rate
            for each credit-consuming metric event_name feeds, summed. Requires
            event_name; incompatible with events, which size the hold
            themselves. Omit it (send reserve: {}) to size the hold to exactly
            the events in this request.
          example: '100000'
          nullable: true
          type: string
      type: object
    CreditBalance:
      properties:
        available:
          description: >-
            What these metrics can still spend from this ledger. When filtered
            is true this is the eligible grant balances net of open holds;
            otherwise it is the plain ledger figure.
          example: '985'
          type: string
        credit_type:
          description: The credit denomination of this entry.
          example: units
          type: string
        filtered:
          description: >-
            True when available reflects the resolved eligible grants net of
            open holds. False when no plan was resolved for this entry (a
            replay, a rate-limit shed, a settlement against a hold, or a
            degraded availability read) and available is the plain ledger
            figure.
          type: boolean
        ledger_id:
          description: >-
            The credit ledger this availability sits on. Omitted when there is
            no wallet to name: no plan was resolved for the entry (a replay, a
            rate-limit shed), no matching grant exists — none was ever made, or
            every one has expired or been voided — or the ledger read behind a
            degraded entry also failed. A merely drained wallet keeps its id, so
            balances[0].ledger_id still answers at the moment credit runs out.
          example: crl_abc
          type: string
        metric_ids:
          description: >-
            The billable metrics this availability applies to. One metric's
            credit can span several ledgers, so sum its entries to reconstruct
            its spendability; two metrics with identical availability share one
            entry.
          example:
            - '["mtr_1"]'
          items:
            type: string
          type: array
        scope:
          allOf:
            - $ref: '#/components/schemas/CreditBalanceScope'
          description: >-
            The scope the entry was resolved under. Omitted when the outcome was
            answered before any plan was resolved (a rate-limit shed) or against
            a hold's own ledgers (a settlement).
          nullable: true
      required:
        - available
        - credit_type
        - filtered
        - metric_ids
      type: object
    CreditCharge:
      properties:
        covered:
          description: >-
            The part of credits that credit covered — drawn on a /track, fenced
            by the hold on a /check with reserve, zero on a quote or preflight
            where nothing moves.
          example: '15'
          type: string
        credits:
          description: What that quantity priced to in credits, at this metric's rate.
          example: '15'
          type: string
        ledger_id:
          description: >-
            The credit ledger the covered part drew from. Empty when nothing was
            covered, and for a metric that consumes no credit. A charge
            cascading across ledgers names the primary (first-drawn) ledger;
            balances carries the per-ledger availability. On an idempotent
            resend of a settle (a replay), per-metric attribution is not
            reconstructable from the recorded close, so every charge names the
            hold's primary ledger — reconcile per-ledger figures from the first
            response, not a replay.
          example: crl_acct
          type: string
        metric_id:
          description: The billable metric this charge counts against.
          example: mtr_tokens
          type: string
        overflow:
          description: >-
            credits − covered: the part billing as cash, in credits. It is an
            exposure figure, not a record of movement — on a quote, a denied
            reserve, or a degraded fail-open nothing moves and covered is zero,
            so this carries the full credits amount: what the work would bill as
            cash if no credit paid for it.
          example: '0'
          type: string
        quantity:
          description: Metric units this call counted for this metric.
          example: '1500'
          type: string
        uncovered_quantity:
          description: >-
            overflow converted back into metric units — the denomination the
            invoice bills them in. For a metric that consumes no credit, the
            full quantity.
          example: '0'
          type: string
      required:
        - covered
        - credits
        - ledger_id
        - metric_id
        - overflow
        - quantity
        - uncovered_quantity
      type: object
    CreditTotals:
      properties:
        covered:
          description: >-
            Credits actually covered (drawn or held), summed across every
            charge.
          example: '23'
          type: string
        overflow:
          description: >-
            Credits not covered that bill as cash, summed across every charge.
            Where nothing was covered — a quote, a denied reserve, a degraded
            fail-open — this equals used: the cash exposure of the described
            work if no credit pays for it.
          example: '0'
          type: string
        uncovered_metric_quantity:
          description: >-
            Metric units that no realtime credit covered and which therefore
            bill as cash, summed across every charge — each converted at its own
            metric's rate. Invoice-time credits may still apply to them.
          example: '0'
          type: string
        used:
          description: >-
            Credits the call priced to, summed across every charge. Computed
            from the events — never supplied by the caller.
          example: '23'
          type: string
      required:
        - covered
        - overflow
        - uncovered_metric_quantity
        - used
      type: object
    CreditBalanceScope:
      properties:
        filters:
          description: >-
            Reserved. A grant's product/price filters are applied when credits
            are applied to an invoice, not by the gate, so this is never set.
          type: object
        level:
          description: >-
            The level this availability was resolved at. Always account: credit
            is resolved across every grant the account holds. A grant made under
            a subscription still funds realtime draws — its subscription records
            where it came from, and does not narrow what it can pay for — so the
            ledger named by an entry may belong to a subscription even though
            the scope is the account.
          enum:
            - account
          example: account
          type: string
        subscription_id:
          description: >-
            Reserved. Availability is never scoped to a subscription, so this is
            never set.
          example: sub_123
          nullable: true
          type: string
      required:
        - level
      type: object
  securitySchemes:
    bearerAuth:
      bearerFormat: API Key
      description: API key authentication. Pass your API key as a Bearer token.
      scheme: bearer
      type: http

````