CognitiveX Docs

Billing & usage

The /api/billing, /api/costs, and /api/admin/analytics/costs endpoints for subscriptions, pay-as-you-go credits, spend caps, usage reporting, and cost analytics.

This page is the contract reference for the billing surface. For a task-oriented walkthrough (choosing a tier, wiring up Checkout, switching to PAYG), read the Billing and usage guide. The per-endpoint generated pages live under API reference.

Base URL: https://api.cognitivx.io. Endpoints here are mounted under /api/billing, /api/costs, and /api/admin/analytics.

Auth model

Every endpoint requires Authorization: Bearer <key> or X-API-Key: <key>, with two exceptions:

  • GET /api/billing/tiers is public (no auth).
  • POST /api/billing/webhooks/stripe authenticates with a Stripe signature, not a bearer key. Do not call it yourself.

Create keys at developers.cognitivx.io/keys. The /api/costs/admin/* and /api/admin/analytics/* endpoints additionally require an admin account.

Error responses are { "detail": "<message>" } with a 4xx/5xx status, except topup and payg-cap validation failures, which return { "error": "...", "message": "..." } (noted per endpoint).

Money on this surface is US dollars (float *_usd fields) or integer credits. The conversion is fixed: 1 credit = $0.0001, so 10,000 credits = $1.00. There is no amount_cents or currency field anywhere in this surface (cents appear only in internal Stripe calls). PAYG spend is OpenRouter cost times a fixed markup (default 1.3x).

Endpoints at a glance

MethodPathAuthPurpose
GET/api/billing/mekeyTier, subscription state, PAYG snapshot
GET/api/billing/tierspublicTier catalog (prices, caps)
POST/api/billing/create-checkoutkeyStart a subscription Checkout
POST/api/billing/verify-checkoutkeyConfirm a Checkout activated the plan
POST/api/billing/change-intervalkeySwitch monthly ↔ annual
POST/api/billing/create-portalkeyOpen the Stripe customer portal
POST/api/billing/switch-to-paygkeyCancel subscription, move to PAYG
POST/api/billing/topupkeyBuy prepaid PAYG credit
GET/api/billing/balancekeyCurrent PAYG credit balance
GET/api/billing/payg-estimatekeyProject PAYG cost vs. current tier
PUT/api/billing/payg-capkeySet or clear the monthly spend cap
GET/api/billing/payg-capkeyRead the monthly spend cap
GET/api/billing/payg-transactionskeyRecent PAYG ledger rows
GET/api/billing/payg-receipts.csvkeyDownload a month's ledger as CSV
GET/api/billing/usagekeyThis month's usage counters
GET/api/billing/usage-historykeyDaily usage buckets
GET/api/billing/memory-capacitykeyStored memories vs. tier cap
GET/api/costs/mekeyCaller's raw (un-marked-up) LLM cost
POST/api/billing/webhooks/stripeStripe sigStripe event ingestion (not caller-facing)
GET/api/costs/admin/usersadminAll-users cost summary
GET/api/costs/admin/user/{user_id}adminOne user's cost detail
GET/api/costs/admin/by_taskadminPer-task / per-model cost aggregate
POST/api/costs/admin/set-tier/{user_id}/{tier}adminForce a user's tier
GET/api/admin/analytics/costsadminMonthly cost by model/user/role
GET/api/admin/analytics/costs/trendsadminCost time series
GET/api/admin/analytics/payg-overflowadminConscious-tier PAYG top-up cohort

Tiers

TierDisplay nameMonthlyAnnualRecall credits / monthMemory cap
amnesiacFree$0—1001,000
awakenedAwakened$20$199500,0001,000,000
consciousConscious$200$1,99915,000,000unlimited
paygPay-as-You-Go$0—unlimited (spend-metered)unlimited

These numbers are illustrative. The live catalog is served by GET /api/billing/tiers; read it for current values.


Subscriptions

Get billing status

Read the caller's current tier, subscription state, and PAYG summary.

GET /api/billing/me · Auth required.

No request body. Returns the tier, Stripe-derived subscription status, trial and renewal dates, the live billing interval, and a PAYG snapshot so a client can render a "Switch to PAYG" CTA without a second call.

curl https://api.cognitivx.io/api/billing/me \
  -H "Authorization: Bearer icog_xxx"
import { configureApiClient, billing } from '@cognitivx/sdk';

configureApiClient({ apiKey: process.env.ICOG_API_KEY });

const status = await billing.me();
// status.tier, status.payg_eligible, status.payg_balance_usd
{
  "tier": "awakened",
  "subscription_status": "active",
  "trial_ends_at": null,
  "renewal_date": "2026-07-16",
  "subscription_interval": "monthly",
  "payg_eligible": true,
  "payg_balance_usd": 0.0,
  "payg_transactions_30d": 0
}

subscription_interval is "monthly", "annual", or null (for amnesiac and payg, which have no subscription) and is resolved live from Stripe. payg_balance_usd is null when the user has never held a credit balance.

Get the tier catalog

Fetch the tier configuration for a pricing page.

GET /api/billing/tiers · No auth required (public).

This is the only public billing endpoint. It returns the tier configuration object (display names, monthly and annual prices, per-month recall-credit caps, and memory caps per tier).

curl https://api.cognitivx.io/api/billing/tiers
{
  "amnesiac": { "display_name": "Free", "price_monthly": 0, "...": "..." },
  "awakened": { "display_name": "Awakened", "price_monthly": 20, "price_annual": 199, "...": "..." },
  "conscious": { "display_name": "Conscious", "price_monthly": 200, "price_annual": 1999, "...": "..." },
  "payg": { "display_name": "Pay-as-You-Go", "price_monthly": 0, "...": "..." }
}

Start a subscription checkout

Create a Stripe Checkout session and redirect the user to pay.

POST /api/billing/create-checkout · Auth required.

FieldTypeRequiredDescription
tierstringyesawakened or conscious. Any other value is rejected.
intervalstringnomonthly (default) or annual.

The response url is a Stripe-hosted Checkout page. Redirect the browser there; on success Stripe sends the user back to your app and a webhook activates the subscription. If the webhook lags, call verify-checkout to close the gap.

curl -X POST https://api.cognitivx.io/api/billing/create-checkout \
  -H "Authorization: Bearer icog_xxx" \
  -H "Content-Type: application/json" \
  -d '{"tier": "awakened", "interval": "monthly"}'
import { configureApiClient, billing } from '@cognitivx/sdk';

const { url } = await billing.createCheckout({
  tier: 'awakened',
  interval: 'monthly',
});
// redirect the browser to `url`
{ "url": "https://checkout.stripe.com/c/pay/cs_test_a1B2c3..." }

Errors

StatusWhen
400tier is not awakened/conscious, or no Stripe price is configured for the tier and interval.
500Stripe customer or checkout-session creation failed.

Verify a checkout

Poll Stripe to confirm a just-completed checkout activated the plan.

POST /api/billing/verify-checkout · Auth required.

FieldTypeRequiredDescription
session_idstringyesThe Checkout session id from the success redirect.

Call this from your success page (the redirect URL carries the session id). It checks Stripe's payment_status and, when paid, flips the user to the purchased tier. Idempotent with the webhook; returns activated: false if the session is not yet paid.

curl -X POST https://api.cognitivx.io/api/billing/verify-checkout \
  -H "Authorization: Bearer icog_xxx" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "cs_test_a1B2c3..."}'
const { activated } = await billing.verifyCheckout({
  session_id: 'cs_test_a1B2c3...',
});
{ "activated": true }

Errors

StatusWhen
400The user has no Stripe customer, or the session belongs to a different customer.
500Stripe session retrieval failed.

Switch monthly and annual

Change an active subscription's billing interval mid-cycle.

POST /api/billing/change-interval · Auth required.

FieldTypeRequiredDescription
intervalstringyesmonthly or annual.

Stripe applies a proration (create_prorations): the unused portion of the current interval is credited against the new interval's invoice. There is no cash refund; the credit or debit lands on the next invoice. If the user is already on the requested interval, the call is a no-op.

curl -X POST https://api.cognitivx.io/api/billing/change-interval \
  -H "Authorization: Bearer icog_xxx" \
  -H "Content-Type: application/json" \
  -d '{"interval": "annual"}'
const res = await billing.changeInterval({ interval: 'annual' });
// res.status is "switched" or "noop"
{
  "status": "switched",
  "message": "Switched to annual. Stripe proration applied; see next invoice.",
  "subscription_id": "sub_1Nxyz...",
  "new_price_id": "price_annual_awakened",
  "previous_price_id": "price_monthly_awakened"
}

When already on the target interval, status is "noop" and previous_price_id is omitted.

Errors

StatusWhen
400The user's tier is not a paid subscription (awakened/conscious), or no price is configured for the tier and interval.
404The Stripe subscription could not be found.
409The user has no active subscription. Use create-checkout for new subscriptions.
502Stripe rejected the modification.

Open the billing portal

Create a Stripe customer-portal session for self-service management.

POST /api/billing/create-portal · Auth required.

No request body. The portal is where customers update cards, view invoices, and cancel. Redirect the browser to the returned url.

curl -X POST https://api.cognitivx.io/api/billing/create-portal \
  -H "Authorization: Bearer icog_xxx"
const { url } = await billing.createPortal();
// redirect the browser to `url`
{ "url": "https://billing.stripe.com/p/session/test_a1B2c3..." }

Errors

StatusWhen
400The user has no Stripe customer (no subscription has ever been started).
500Stripe portal-session creation failed.

Invoices are not served by the CognitiveX API. They live in Stripe; send users to the portal (create-portal) to view and download them. For a machine-readable PAYG spend export, use payg-receipts.csv.


Pay-as-you-go (PAYG)

PAYG bills metered spend against a prepaid credit balance instead of a flat monthly subscription. Users top up a dollar balance, spend it down, and can cap their monthly spend. Top-ups are whole dollars ($5–$500); spend caps range $1–$10,000.

Switch to PAYG

Cancel any active subscription and move the user to metered billing.

POST /api/billing/switch-to-payg · Auth required.

FieldTypeRequiredDescription
refund_methodstringnocredit (default) leaves the proration on the Stripe customer balance, applied to the next charge. card refunds the most recent subscription charge to the original payment method.

Behavior depends on the current tier. amnesiac just flips the tier (no subscription to cancel). awakened/conscious cancels the active subscription immediately with proration. payg is idempotent and returns already_payg: true.

curl -X POST https://api.cognitivx.io/api/billing/switch-to-payg \
  -H "Authorization: Bearer icog_xxx" \
  -H "Content-Type: application/json" \
  -d '{"refund_method": "credit"}'
const res = await billing.switchToPayg({ refund_method: 'credit' });
// res.new_tier === "payg"; res.requires_topup tells you whether to prompt
{
  "already_payg": false,
  "refunded_usd": 12.33,
  "refund_method": "credit",
  "new_tier": "payg",
  "requires_topup": true
}

requires_topup is true when the credit balance is <= 0 after switching, so the client knows to prompt for a top-up before the user can spend. refund_method in the response is credit, card, or none.

Errors

StatusWhen
404User not found.
502Stripe could not be reached to list or cancel the subscription.

Top up credits

Create a Stripe PaymentIntent to add credit to the PAYG balance.

POST /api/billing/topup · Auth required.

FieldTypeRequiredDescription
amount_usdintegeryesWhole dollars to add. Minimum $5, maximum $500.
intent_to_switchbooleannoDefault false. Set true when this top-up is part of an "amnesiac switches to PAYG" flow; the webhook then auto-promotes the user on payment.

Returns a Stripe client_secret and the PaymentIntent id. Confirm the payment client-side with Stripe.js; the credit lands on the balance once the payment succeeds (handled by webhook). amount_usd is converted to credits at 10,000 credits per dollar.

curl -X POST https://api.cognitivx.io/api/billing/topup \
  -H "Authorization: Bearer icog_xxx" \
  -H "Content-Type: application/json" \
  -d '{"amount_usd": 25}'
const { client_secret } = await billing.topup({ amount_usd: 25 });
// confirm `client_secret` with Stripe.js
{
  "client_secret": "pi_3Nxyz..._secret_abc",
  "payment_intent_id": "pi_3Nxyz..."
}

Errors

StatusWhen
422amount_usd is below $5 or above $500. Body is {"error": "invalid_amount", "message": "..."}.

Get balance

Read the current PAYG credit balance and low-balance flags.

GET /api/billing/balance · Auth required.

No request body. balance_credits is canonical; balance_usd is the same value times 0.0001 and is kept for back-compat. low_balance is true when the balance drops below 20,000 credits ($2). A user with no credit row gets all-zero defaults.

curl https://api.cognitivx.io/api/billing/balance \
  -H "Authorization: Bearer icog_xxx"
const { balance_credits, low_balance, depleted } = await billing.balance();
{
  "balance_credits": 245000,
  "balance_usd": 24.5,
  "low_balance": false,
  "depleted": false
}

PAYG estimate

Project the last 30 days of usage as a PAYG cost and compare it to the current tier price.

GET /api/billing/payg-estimate · Auth required.

No request body. Use this to show a "would PAYG be cheaper?" panel. The markup multiplier (default 1.3) and a per-model breakdown are included.

curl https://api.cognitivx.io/api/billing/payg-estimate \
  -H "Authorization: Bearer icog_xxx"
{
  "raw_spend_30d_usd": 8.42,
  "projected_payg_30d_usd": 10.95,
  "current_tier": "awakened",
  "current_tier_price_usd": 20.0,
  "effective_current_price_usd": 20.0,
  "savings_vs_current_usd": 9.05,
  "markup_multiplier": 1.3,
  "model_breakdown": [
    {
      "model": "gemini-2.5-flash",
      "tokens_in": 412000,
      "tokens_out": 88000,
      "cost_raw_usd": 6.10,
      "cost_payg_usd": 7.93
    }
  ]
}

effective_current_price_usd is 0 while the user is on a trial, on the free tier, or canceled.

Set the PAYG spend cap

Set or clear a monthly PAYG spend cap. Requests hard-block once the cap is reached.

PUT /api/billing/payg-cap · Auth required.

FieldTypeRequiredDescription
monthly_cap_usdnumber | nullyesThe cap in dollars ($1–$10,000), or null to clear it.

Stored internally as credits and enforced at request time. When the month-to-date spend reaches the cap, further metered requests are blocked (monthly_cap_reached).

curl -X PUT https://api.cognitivx.io/api/billing/payg-cap \
  -H "Authorization: Bearer icog_xxx" \
  -H "Content-Type: application/json" \
  -d '{"monthly_cap_usd": 50}'
await billing.setPaygCap({ monthly_cap_usd: 50 });
// clear it with: await billing.setPaygCap({ monthly_cap_usd: null });
{ "monthly_cap_usd": 50.0, "spent_this_month_usd": 12.4 }

Errors

StatusWhen
422monthly_cap_usd is below $1 or above $10,000. Body is {"error": "invalid_cap", "message": "..."}.

Get the PAYG spend cap

Read the current monthly cap and month-to-date spend.

GET /api/billing/payg-cap · Auth required.

No request body. monthly_cap_usd is null when no cap is set.

curl https://api.cognitivx.io/api/billing/payg-cap \
  -H "Authorization: Bearer icog_xxx"
const { monthly_cap_usd, spent_this_month_usd } = await billing.getPaygCap();
{ "monthly_cap_usd": 50.0, "spent_this_month_usd": 12.4 }

List PAYG transactions

Read recent PAYG ledger rows for a transaction list.

GET /api/billing/payg-transactions · Auth required.

ParamInTypeRequiredDescription
limitqueryintegernoDefault 30. Clamped to 1–100.

event_type is spend, topup, proration_credit, and similar. Spend rows carry a negative amount_usd and a model; non-spend rows have model: null.

curl 'https://api.cognitivx.io/api/billing/payg-transactions?limit=10' \
  -H "Authorization: Bearer icog_xxx"
const { transactions } = await billing.paygTransactions(10);
{
  "transactions": [
    {
      "id": 4821,
      "event_type": "spend",
      "amount_usd": -0.0123,
      "balance_after": 24.4877,
      "model": "gemini-2.5-flash",
      "created_at": "2026-06-16T14:02:11.481000+00:00"
    },
    {
      "id": 4795,
      "event_type": "topup",
      "amount_usd": 25.0,
      "balance_after": 24.5,
      "model": null,
      "created_at": "2026-06-15T09:10:00.000000+00:00"
    }
  ]
}

Export PAYG receipts

Download a month of PAYG ledger rows as CSV.

GET /api/billing/payg-receipts.csv · Auth required.

ParamInTypeRequiredDescription
monthquerystringnoYYYY-MM. Defaults to the current month.

Returns text/csv with a Content-Disposition: attachment header. Columns: timestamp_utc, event_type, amount_usd, balance_after_usd, model, stripe_id.

curl 'https://api.cognitivx.io/api/billing/payg-receipts.csv?month=2026-06' \
  -H "Authorization: Bearer icog_xxx" -OJ
timestamp_utc,event_type,amount_usd,balance_after_usd,model,stripe_id
2026-06-15T09:10:00+00:00,topup,25.000000,25.0000,,pi_3Nxyz...
2026-06-16T14:02:11.481000+00:00,spend,-0.012300,24.4877,gemini-2.5-flash,

Errors

StatusWhen
422month is not in YYYY-MM format.

Usage reporting

Current-month usage

This month's usage counters, recall-credit breakdown, tier cap, and display helpers.

GET /api/billing/usage · Auth required.

No request body. recall_credits_per_month is null for PAYG. recall_credit_breakdown always carries all five recall modes. show_onboarding_explainer is a one-shot flag (cleared after it is read once).

curl https://api.cognitivx.io/api/billing/usage \
  -H "Authorization: Bearer icog_xxx"
const usage = await billing.usage();
{
  "message": 312,
  "memory_write": 47,
  "image_gen": 3,
  "recall_credit_count": 18420,
  "recall_credits_per_month": 500000,
  "recall_credit_breakdown": {
    "foundational": 1200,
    "quick": 4100,
    "standard": 9800,
    "deep": 3000,
    "identity": 320
  },
  "cap_reset_at": "2026-07-01T00:00:00Z",
  "memories_saved_this_month": 47,
  "show_onboarding_explainer": false
}

Daily usage history

Daily usage buckets for billing charts.

GET /api/billing/usage-history · Auth required.

ParamInTypeRequiredDescription
daysqueryintegernoDefault 30. Clamped to 1–90.
curl 'https://api.cognitivx.io/api/billing/usage-history?days=7' \
  -H "Authorization: Bearer icog_xxx"
const { days, history } = await billing.usageHistory(7);
{
  "days": 7,
  "history": [
    {
      "date": "2026-06-16",
      "messages": 42,
      "memory_writes": 6,
      "images": 1,
      "recalls": 88,
      "chats": 42,
      "dreams": 1,
      "insights": 0,
      "lens_regens": 0,
      "mcp_calls": 12,
      "other_actions": 3,
      "credits": 2640
    }
  ]
}

Memory capacity

Total stored-memory count vs. the tier cap.

GET /api/billing/memory-capacity · Auth required.

No request body. conscious and payg are uncapped (the cap field is null).

curl https://api.cognitivx.io/api/billing/memory-capacity \
  -H "Authorization: Bearer icog_xxx"
const capacity = await billing.memoryCapacity();

My LLM cost breakdown

The caller's own raw (un-marked-up) LLM cost for a month.

GET /api/costs/me · Auth required.

ParamInTypeRequiredDescription
monthquerystringnoYYYY-MM. Defaults to the current month.

This is the raw OpenRouter cost, not the marked-up PAYG price. To project what PAYG would charge, use payg-estimate.

curl 'https://api.cognitivx.io/api/costs/me?month=2026-06' \
  -H "Authorization: Bearer icog_xxx"

Errors

StatusWhen
503The cost tracker is not initialized.

Stripe webhook

Stripe-to-server event ingestion: subscription activation, top-up credit, and cancellation. Not caller-facing.

POST /api/billing/webhooks/stripe · Stripe signature auth (no bearer).

Authenticated by the stripe-signature header against the raw event body. The endpoint claims each event idempotently and processes it in the background.

{ "received": true }

Errors

StatusWhen
400Missing stripe-signature header, invalid payload, or signature verification failed.

Do not call this endpoint directly. It is the Stripe-to-server channel and rejects anything without a valid Stripe signature.


Admin: cost analytics

These endpoints require an admin account (require_admin). They are documented for completeness and are not part of the normal developer surface.

All-users cost summary

GET /api/costs/admin/users · Admin.

ParamInTypeRequiredDescription
monthquerystringnoYYYY-MM. Defaults to the current month.

Cost summary for all users with emails, sorted by cost descending. Returns 503 if the cost tracker is not initialized.

One user's cost detail

GET /api/costs/admin/user/{user_id} · Admin.

ParamInTypeRequiredDescription
user_idpathstringyesTarget user id.
monthquerystringnoYYYY-MM. Defaults to the current month.

Detailed cost breakdown for one user. Returns 503 if the cost tracker is not initialized.

Cost by task

GET /api/costs/admin/by_task · Admin.

ParamInTypeRequiredDescription
monthquerystringnoYYYY-MM. Defaults to the current month.

Per-task and per-model aggregate cost: count, mean, standard deviation, min/max, token means, p50/p95 latency, fallback rate, and cumulative totals.

Set a user's tier

POST /api/costs/admin/set-tier/{user_id}/{tier} · Admin.

ParamInTypeRequiredDescription
user_idpathstringyesTarget user id, or me for self.
tierpathstringyesamnesiac, awakened, or conscious.

Force a user's tier. PAYG is not a settable target here.

curl -X POST 'https://api.cognitivx.io/api/costs/admin/set-tier/me/awakened' \
  -H "Authorization: Bearer icog_admin_xxx"
{
  "user_id": "<uuid>",
  "tier": "awakened",
  "message": "Tier set to awakened"
}

Errors

StatusWhen
400tier is not one of amnesiac, awakened, conscious.

Monthly cost breakdown

GET /api/admin/analytics/costs · Admin.

ParamInTypeRequiredDescription
monthquerystringnoYYYY-MM. Defaults to the current month.

Monthly cost by model, user, and role, with budget status. budget_status is null unless a monthly budget is configured server-side. If the cost tracker is missing, this endpoint returns zeros with a "Cost tracking not enabled" message (not a 503).

{
  "month": "2026-06",
  "total_usd": 0,
  "system_usd": 0,
  "user_count": 0,
  "by_model": {},
  "by_user": [],
  "by_role": {},
  "budget_status": null
}

GET /api/admin/analytics/costs/trends · Admin.

ParamInTypeRequiredDescription
periodquerystringnodaily (default), weekly, or monthly.
monthsqueryintegernoDefault 3. Range 1–12.

Cost time series for trend charts.

{
  "period": "daily",
  "months": 3,
  "data": [
    { "date": "2026-06-16", "cost_usd": 0, "tokens_in": 0, "tokens_out": 0 }
  ]
}

PAYG overflow cohort

GET /api/admin/analytics/payg-overflow · Admin.

No params. Conscious subscribers who also buy PAYG top-ups (active Conscious plus at least $5 in top-ups over a trailing 90 days): counts, MRR, M1/M3 retention, and top spenders. Retention fields can be null.

{
  "active_overflow_count": 0,
  "conscious_total": 0,
  "overflow_pct": 0,
  "avg_topup_mrr": 0,
  "avg_total_mrr": 0,
  "retention_m1_overflow": null,
  "top_spenders": [
    {
      "user_id_prefix": "a1b2c3d4",
      "recent_topup_total_usd": 0,
      "estimated_total_mrr": 0
    }
  ]
}