Skip to content

Search docs

Find pages, headings, and concepts. Press ⌘K or Ctrl+K to toggle.

Billing

Read org balance, usage, subscription plans, transactions, and deposits.

Billing combines subscription plans with a real USD balance held in cents. Token usage is recorded in microcents (1 cent = 10,000 microcents). Deposits raise both balanceCents and cumulativeDepositsCents; the platform fee is waived until cumulative deposits exceed the configured threshold.

Endpoints

MethodPathDescription
GET/api/v1/billingBalance, current-month usage, recent transactions, waiver status.
PATCH/api/v1/billingConfigure auto top-up. Requires billing-manage permission.
POST/api/v1/billing/depositStart a deposit (Stripe Checkout or direct in non-production).
POST/api/v1/billing/subscriptions/checkoutStart Stripe Checkout for a monthly Alumia plan.
GET/api/v1/billing/transactionsPaginated transaction history.
GET/api/v1/billing/pricingSubscription plans, per-model pricing with platform fee applied, plus service prices.
GET/api/v1/billing/spendingSpending summary, daily series, and top agents/models.
GET/api/v1/billing/spending/by-modelSpending grouped by model.
GET/api/v1/billing/spending/by-agentSpending grouped by agent.
GET/api/v1/billing/spending/by-projectSpending grouped by project.
GET/api/v1/billing/byok-keysList legacy provider-key entries.
POST/api/v1/billing/byok-keysDisabled by default while provider-key setup is unavailable.
DELETE/api/v1/billing/byok-keys/:idRemove a legacy provider-key entry.

Balance and usage summary

GET /api/v1/billing returns the org's billing snapshot:

{
  "success": true,
  "data": {
    "balance": {
      "balanceCents": 0,
      "cumulativeDepositsCents": 0,
      "autoTopupEnabled": false,
      "autoTopupThresholdCents": 500,
      "autoTopupAmountCents": 2500
    },
    "waiver": { "active": true, "remainingCents": 10000 },
    "payment": {
      "stripeConfigured": true,
      "webhookConfigured": true,
      "publishableKeyConfigured": true,
      "directDepositAvailable": false,
      "billingEnforced": true
    },
    "usage": {
      "currentMonth": {
        "inputTokens": 0,
        "outputTokens": 0,
        "cacheReadTokens": 0,
        "cacheWriteTokens": 0,
        "providerCostMicrocents": 0,
        "platformFeeMicrocents": 0,
        "totalChargedMicrocents": 0,
        "totalChargedCents": 0,
        "requestCount": 0,
        "byokRequestCount": 0,
        "totalTokens": 0,
        "monthStart": "2026-05-01T00:00:00.000Z",
        "operationBreakdown": [
          { "operation": "chat", "requestCount": 12, "totalChargedMicrocents": 1234, "totalChargedCents": 1 }
        ]
      }
    },
    "transactions": [
      { "id": "uuid", "type": "deposit", "amountCents": 1000, "stripePaymentIntentId": "pi_...", "notes": null, "createdAt": "..." }
    ],
    "promotions": []
  }
}

Update auto top-up

PATCH /api/v1/billing

FieldTypeDescription
autoTopupEnabledbooleanToggle automatic top-ups when balance drops below threshold.
autoTopupThresholdCentsintegerMinimum 100 (=$1.00).
autoTopupAmountCentsintegerMinimum 100 (=$1.00).

Requires the caller to pass assertCanManageBilling. Returns { "updated": true }.

Deposit

POST /api/v1/billing/deposit

FieldTypeRequiredDescription
amountCentsintegeryes100 ≤ x ≤ 100,000 (i.e. $1.00–$1,000.00).
method"direct"noOnly honored in non-production when Stripe is unconfigured or the env opts in.

When Stripe is configured, the response is a Checkout Session:

{ "success": true, "data": { "checkoutUrl": "https://...", "sessionId": "cs_..." } }

Redirect the user to checkoutUrl. On completion, Stripe sends a webhook to /api/v1/billing/webhooks/stripe, which credits the balance and writes a deposit transaction.

When Stripe is not configured (local/dev), the response is:

{ "success": true, "data": { "deposited": 1000, "method": "direct", "message": "Deposit applied directly (Stripe not configured)" } }
bash
Sign in to fill in your org and key
curl -X POST https://alumia.com/api/v1/billing/deposit \
-H "Authorization: Bearer alm_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "amountCents": 1000 }'

Subscription checkout

POST /api/v1/billing/subscriptions/checkout

FieldTypeRequiredDescription
planIdstringyesOne of the public Alumia monthly plan IDs returned by /api/v1/billing/pricing.

When Stripe is configured, the response is:

{ "success": true, "data": { "checkoutUrl": "https://...", "sessionId": "cs_...", "planId": "team" } }

Redirect the user to checkoutUrl. Custom plans return a validation error and are handled by sales.

Transactions

GET /api/v1/billing/transactions?type=&limit=&page= — Paginated list. Valid type values: deposit, usage, refund, adjustment, platform_fee. Unknown values fall back to no filter.

Pricing

GET /api/v1/billing/pricing — Returns subscription plans, the platform fee rate, per-model rates with the fee already applied, and the list of billable service prices.

Spending analytics

GET /api/v1/billing/spending?period= returns a summary, daily series, and the top agents and models by spend. The period query accepts today, week, month, year, etc. (normalized server-side). Sub-routes (by-model, by-agent, by-project) return single-axis breakdowns over the same period.

Legacy provider keys

GET /api/v1/billing/byok-keys returns the caller's legacy keys (provider, label, fingerprint), never the plaintext.

POST /api/v1/billing/byok-keys is disabled by default and returns BAD_REQUEST while new provider-key setup is unavailable. It can be explicitly re-enabled for controlled maintenance with ALUMIA_BYOK_ENABLED=1.

FieldTypeRequiredDescription
providerstringyesMust be one of BYOK_PROVIDERS (Anthropic, OpenAI, etc.).
keystringyes16–500 chars.
labelstringnoUp to 200 chars.

DELETE /api/v1/billing/byok-keys/:id removes a legacy entry.

Errors

CodeWhen
UNAUTHORIZEDNo valid key.
PAYMENT_REQUIREDOrg cannot use paid models because the balance is exhausted. (Surfaced from chat routes, not billing reads.)
FORBIDDENCaller lacks billing-manage role for PATCH, POST /deposit, or subscription checkout.
BAD_REQUESTAmount out of range, invalid auto top-up values, unavailable provider-key setup, unknown subscription plan, or rate limit exceeded.