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/tiersis public (no auth).POST /api/billing/webhooks/stripeauthenticates 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
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/billing/me | key | Tier, subscription state, PAYG snapshot |
| GET | /api/billing/tiers | public | Tier catalog (prices, caps) |
| POST | /api/billing/create-checkout | key | Start a subscription Checkout |
| POST | /api/billing/verify-checkout | key | Confirm a Checkout activated the plan |
| POST | /api/billing/change-interval | key | Switch monthly ↔ annual |
| POST | /api/billing/create-portal | key | Open the Stripe customer portal |
| POST | /api/billing/switch-to-payg | key | Cancel subscription, move to PAYG |
| POST | /api/billing/topup | key | Buy prepaid PAYG credit |
| GET | /api/billing/balance | key | Current PAYG credit balance |
| GET | /api/billing/payg-estimate | key | Project PAYG cost vs. current tier |
| PUT | /api/billing/payg-cap | key | Set or clear the monthly spend cap |
| GET | /api/billing/payg-cap | key | Read the monthly spend cap |
| GET | /api/billing/payg-transactions | key | Recent PAYG ledger rows |
| GET | /api/billing/payg-receipts.csv | key | Download a month's ledger as CSV |
| GET | /api/billing/usage | key | This month's usage counters |
| GET | /api/billing/usage-history | key | Daily usage buckets |
| GET | /api/billing/memory-capacity | key | Stored memories vs. tier cap |
| GET | /api/costs/me | key | Caller's raw (un-marked-up) LLM cost |
| POST | /api/billing/webhooks/stripe | Stripe sig | Stripe event ingestion (not caller-facing) |
| GET | /api/costs/admin/users | admin | All-users cost summary |
| GET | /api/costs/admin/user/{user_id} | admin | One user's cost detail |
| GET | /api/costs/admin/by_task | admin | Per-task / per-model cost aggregate |
| POST | /api/costs/admin/set-tier/{user_id}/{tier} | admin | Force a user's tier |
| GET | /api/admin/analytics/costs | admin | Monthly cost by model/user/role |
| GET | /api/admin/analytics/costs/trends | admin | Cost time series |
| GET | /api/admin/analytics/payg-overflow | admin | Conscious-tier PAYG top-up cohort |
Tiers
| Tier | Display name | Monthly | Annual | Recall credits / month | Memory cap |
|---|---|---|---|---|---|
amnesiac | Free | $0 | — | 100 | 1,000 |
awakened | Awakened | $20 | $199 | 500,000 | 1,000,000 |
conscious | Conscious | $200 | $1,999 | 15,000,000 | unlimited |
payg | Pay-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.
| Field | Type | Required | Description |
|---|---|---|---|
tier | string | yes | awakened or conscious. Any other value is rejected. |
interval | string | no | monthly (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
| Status | When |
|---|---|
| 400 | tier is not awakened/conscious, or no Stripe price is configured for the tier and interval. |
| 500 | Stripe 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.
| Field | Type | Required | Description |
|---|---|---|---|
session_id | string | yes | The 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
| Status | When |
|---|---|
| 400 | The user has no Stripe customer, or the session belongs to a different customer. |
| 500 | Stripe session retrieval failed. |
Switch monthly and annual
Change an active subscription's billing interval mid-cycle.
POST /api/billing/change-interval · Auth required.
| Field | Type | Required | Description |
|---|---|---|---|
interval | string | yes | monthly 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
| Status | When |
|---|---|
| 400 | The user's tier is not a paid subscription (awakened/conscious), or no price is configured for the tier and interval. |
| 404 | The Stripe subscription could not be found. |
| 409 | The user has no active subscription. Use create-checkout for new subscriptions. |
| 502 | Stripe 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
| Status | When |
|---|---|
| 400 | The user has no Stripe customer (no subscription has ever been started). |
| 500 | Stripe 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.
| Field | Type | Required | Description |
|---|---|---|---|
refund_method | string | no | credit (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
| Status | When |
|---|---|
| 404 | User not found. |
| 502 | Stripe 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.
| Field | Type | Required | Description |
|---|---|---|---|
amount_usd | integer | yes | Whole dollars to add. Minimum $5, maximum $500. |
intent_to_switch | boolean | no | Default 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
| Status | When |
|---|---|
| 422 | amount_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.
| Field | Type | Required | Description |
|---|---|---|---|
monthly_cap_usd | number | null | yes | The 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
| Status | When |
|---|---|
| 422 | monthly_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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | no | Default 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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
month | query | string | no | YYYY-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" -OJtimestamp_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
| Status | When |
|---|---|
| 422 | month 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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
days | query | integer | no | Default 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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
month | query | string | no | YYYY-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
| Status | When |
|---|---|
| 503 | The 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
| Status | When |
|---|---|
| 400 | Missing 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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
month | query | string | no | YYYY-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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
user_id | path | string | yes | Target user id. |
month | query | string | no | YYYY-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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
month | query | string | no | YYYY-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.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
user_id | path | string | yes | Target user id, or me for self. |
tier | path | string | yes | amnesiac, 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
| Status | When |
|---|---|
| 400 | tier is not one of amnesiac, awakened, conscious. |
Monthly cost breakdown
GET /api/admin/analytics/costs · Admin.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
month | query | string | no | YYYY-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
}Cost trends
GET /api/admin/analytics/costs/trends · Admin.
| Param | In | Type | Required | Description |
|---|---|---|---|---|
period | query | string | no | daily (default), weekly, or monthly. |
months | query | integer | no | Default 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
}
]
}Related
- Billing and usage guide — the task-oriented walkthrough.
- Authentication & keys — how to authenticate every request.
- API reference — the full generated endpoint index.