Billing & Earnings — overview
How Imperal Cloud bills users and how developers earn revenue from their extensions
This section is for extension developers publishing on the Imperal Marketplace. It covers how users pay, how your extension earns, and how you get paid out.
Not building extensions? This section is for marketplace publishers. If you're a regular Imperal Cloud user looking at your own bill, log into the Panel → Billing tab.
The three things users pay for
Imperal users pay along three independent surfaces:
┌──────────────────────────────────────────────────────────────┐
│ Tier 1: Platform subscription │
│ Flat monthly/annual fee for Panel access. │
│ Charged via Stripe recurring (auto-renew). │
├──────────────────────────────────────────────────────────────┤
│ Tier 2: Token wallet (pay-as-you-go) │
│ Replenishable token balance — top up via Stripe one-shot. │
│ Tokens get consumed on every action call. │
├──────────────────────────────────────────────────────────────┤
│ Tier 3: Extension rental (planned — not yet enforced) │
│ Optional monthly token subscription per paid extension. │
└──────────────────────────────────────────────────────────────┘As a developer, you earn from Tier 2 — every time a user invokes one of your extension's @chat.function handlers, tokens flow from their wallet, and a percentage accrues to your developer balance. Your developer tier controls the split, app cap, payout access, analytics window, and rate limit:
| Tier | Split (dev / platform) | Max apps | Payout | Analytics window | Rate limit | Annual price |
|---|---|---|---|---|---|---|
explorer (default) | 70 / 30 | 1 | ❌ accrue only | 7 days | 100 req/min | Free |
indie | 80 / 20 | 3 | ✅ | 30 days | 500 req/min | 9,000 tokens / year |
studio | 85 / 15 | 10 | ✅ | 90 days | 2,000 req/min | 29,000 tokens / year |
partner (admin-promoted) | 95 / 5 | unlimited | ✅ | 365 days | 5,000 req/min | 79,000 tokens / year |
Default for new developers is explorer. Tier subscriptions auto-renew once a year; lapse drops you back to explorer (existing earnings stay in your balance). See Developer Tiers for break-even math, upgrade flow, and tier-lock semantics on existing apps.
What your extension can charge for
Every @chat.function in your extension has an action_type (read / write / destructive). When a user invokes it:
- Web-kernel resolves the token cost from your pricing config (Redis key
imperal:billing:pricing:{app_id}). - Cost =
base_price(from your pricing) +platform_fee(covers Imperal's LLM cost; zero for BYOLLM users — see BYOLLM Pricing). - Tokens deducted atomically from user wallet via Lua script.
- Audit event written to
imperal:billing:eventsstream. - Billing consumer writes double-entry rows to
token_ledgerand yourdeveloper_earningsrow.
You see the result in Dev Portal → My App → Earnings.
What you DON'T charge for
These flows bypass your earnings — they're either platform-side or web-kernel-internal:
- Skeleton refreshes (
@ext.skeletondecorator) — free, web-kernel runs them in background; never billed. - Panel renders (
@ext.panelhandlers) — free, also background-driven. - Branch B conversational turns — when user chats without invoking a tool, web-kernel charges
__system__/hub_chat/write(not your extension). For BYOLLM users this is zero. - Cancelled / refunded actions — saga compensation refunds the user; your earnings row is reversed.
Quick reference
| What | Where to read |
|---|---|
| Set prices for your extension | Pricing Models |
| Tier system + revenue splits (70 → 95%) | Developer Tiers |
| How earnings are tracked + viewed | Revenue & Earnings |
| BYOLLM impact on your revenue | BYOLLM Pricing ← important |
| Request a payout | Payouts |
| Pricing UI walkthrough | Dev Portal Publishing guide |
Lifecycle of pricing edits
App status: draft → pending_review → active
───── ─────────────── ──────
Pricing: editable locked (review) locked (must `Pause` to edit)To change prices on an active app: Pause in Dev Portal → edit → Resubmit for Review → admin re-approves. Prices propagate to Redis on approval.
Real-time observability
In Dev Portal → Earnings:
- Total Earned — sum of all
developer_shareacrossdeveloper_earnings(Redis cache:imperal:developer:earnings:{your_id}) - Pending Payout — earned minus what's already paid/approved
- Paid Out — sum of
developer_payouts WHERE status IN ('approved','paid')
Updates within seconds of each user call (consumer-driven).
Federal billing invariants (live since 2026-05-12)
Five federal contracts pin the correctness of the billing critical path. They are enforced by tests in imperal-web-kernel/tests/test_i_* and by static-source assertions in imperal_kernel/_invariant_assertions.py. CI fails any commit that drops them.
| Invariant | What it pins | Why it matters to you |
|---|---|---|
I-BILLING-ENFORCE-UNIFIED | Every billable code path goes through a single deduct_for_action() chokepoint; legacy direct-wallet writes are forbidden. BILLING_ENFORCE=true in the platform-worker + session-worker .env is now the production posture. | Your @chat.function always charges the user via the same dispatch logic — you cannot accidentally skip billing in a refactor, and pricing changes propagate uniformly across single-action, chain-reserve, and Branch B paths. |
I-WALLET-PERSISTENT | All four Redis Lua wallet scripts (CHECK_AND_DEDUCT, RESERVE, SETTLE, CREDIT) call PERSIST KEYS[1] atomically. Wallet keys are TTL-free by construction. | Your earnings row is never lost to a stray TTL on the source wallet key. A wallet that "disappears" is a federal incident, not a stale-cache UX bug. |
I-WALLET-SWEEP-ACTIVE | A Temporal WalletPersistSweepWorkflow runs hourly in the imperal-billing namespace and re-runs PERSIST on any wallet keys that somehow regained a TTL (external Redis maintenance scripts, etc.). | Defence-in-depth — even if a third-party tool tries to apply a blanket EXPIRE, the sweep restores correctness within the hour. No cron — Temporal only (federal rule). |
I-BYOLLM-PLATFORM-FEE-ZERO | When ctx.user.is_byollm == True, platform_fee → 0 at every deduct site (single-action, chain reserve, Branch B conversational). Resolved at deduct time via async lookup of user_llm_config store, with 60s Redis cache imperal:byollm_user:{user_id}. | Your base_price is unchanged; only the platform component drops. See BYOLLM Pricing for the full impact on earnings. |
I-NARRATOR-NO-FABRICATED-TOKEN-CLAIMS | The web-kernel narrator MUST NOT emit cost claims to the user unless the claim is sourced from the actual deduct event. A regex+whitelist detector at the delivery chokepoint strips fabricated phrases like "this cost you 12 tokens" when no deduct row backs them. | What the user sees in chat matches what they were actually charged. Your extension's pricing config is the only source of truth — the narrator cannot make up a different number. |
Spec: superpowers/specs/2026-05-12-v5-92-enf-billing-2-rollout.md. See BYOLLM Pricing for the developer-facing impact of the first four invariants.