Billing
Plans, seats, AI usage in US dollars, AI top-ups, auto-recharge and Stripe checkout on opendesk Cloud.
Cloud only. Billing exists only on opendesk Cloud. See Cloud plans and billing. On an instance without Stripe configured, every route below returns 404 billing_disabled, except GET /v1/billing, which returns the self-hosted entitlements (plan self_hosted, no limits and unlimited AI), and GET /v1/billing/usage.
| Method | Path | Who | Description |
|---|---|---|---|
GET |
/v1/billing |
member | The workspace’s plan, limits, usage and AI balance |
POST |
/v1/billing/checkout |
owner, admin | { interval: "month" | "year" } → { url } to Stripe Checkout |
POST |
/v1/billing/portal |
owner, admin | { url } to the Stripe customer portal |
POST |
/v1/billing/top-up/checkout |
owner, admin | { top_ups: 1..20 } → { url } to a one-time Checkout for AI top-ups ($10 of AI usage each) |
PATCH |
/v1/billing/auto-recharge |
owner, admin | { enabled?, threshold_usd?, top_ups?, monthly_limit? } → the billing object. See Auto-recharge |
POST |
/v1/billing/payment-method |
owner, admin | { url } to Stripe Checkout in setup mode, to save a card |
GET |
/v1/billing/usage?from=&to= |
admin | AI usage by feature, day, member and model; also on self-hosted |
Checkout returns to /settings/billing?checkout=success|canceled, an AI top-up to ?top_up=success|canceled, and saving a card to ?payment_method=success|canceled. The plan and balance change when Stripe confirms the payment, not on the redirect: wait for billing.updated, or fetch GET /v1/billing again.
The billing object
Section titled “The billing object”Every amount ending in _usd, or named usd, is US dollars, a number with up to 6 decimals.
interface Billing { plan: "free" | "pro" | "self_hosted"; billing_enabled: boolean; status: "none" | "trialing" | "active" | "past_due" | "unpaid" | "canceled" | "incomplete" | "incomplete_expired" | "paused"; interval: "month" | "year"; seats: number; // billed seats in Stripe billed_seats: number; // current owners, admins and agents current_period_end: string | null; cancel_at_period_end: boolean; limits: { max_mailboxes: number; max_members: number }; // 0 = unlimited usage: { mailboxes: number; members: number }; ai: { unlimited: boolean; // self-hosted: no allowance, nothing refused monthly_usd: number; // the monthly allowance: $0.50 on Free, $10 per paid seat on Pro used_usd: number; // used of the allowance this period purchased_usd: number; // AI top-ups left; they never expire and are spent after the allowance remaining_usd: number; // what is left of the allowance, plus purchased_usd period_end: string; // the allowance resets at the start of each calendar month, UTC }; prices: { pro_month: Price | null; pro_year: Price | null; ai_top_up: Price | null }; auto_recharge: AutoRecharge | null; // null when billing is off}
interface Price { id: string; unit_amount: number; // in minor units, such as cents currency: string; interval: "month" | "year" | null; usd?: number; // ai_top_up: the AI usage one top-up adds (10)}
interface AutoRecharge { enabled: boolean; // off by default threshold_usd: number; // recharge when the AI balance falls below this (default 2) top_ups: number; // AI top-ups per recharge, 1..20 (default 1) monthly_limit: number; // recharges per calendar month, UTC, 1..30 (default 5) recharges_this_month: number; low_at_usd: number; // a balance below this reads as running low last_recharge: { at: string; top_ups: number; usd: number; amount: number; currency: string } | null; // amount charged, in minor units failure: { code: "card_declined" | "authentication_required" | "no_payment_method" | "payment_failed" | "unavailable"; at: string } | null;}Plans and limits
Section titled “Plans and limits”- Every feature is on every plan. Plans differ only in limits.
- Seats are members with the owner, admin or agent role. Viewers are free.
- When a limit is reached, creating another mailbox or member returns
402 plan_limit, with the limit named indetail. Nothing existing is removed.
AI usage
Section titled “AI usage”- AI usage is charged in US dollars on each call’s real cost: the provider’s cost at the model’s list price, times 1.5 on opendesk Cloud, kept to $0.000001. The charge depends on the model the workspace chose (see Models) and on the size of the request.
- A call starts only when the balance covers its estimate: the prompt tokens at the model’s input price plus the call’s maximum output tokens (counted up to 4,000) at its output price, times the markup, and at least $0.01. Otherwise the route returns
402 ai_balance_emptyand the model is not called. If a call ends up costing more than what is left, the balance goes to $0. - The allowance is spent first, then AI top-ups, which never expire. A failed call costs nothing. Search indexing is free.
- Calls on the workspace’s own provider key cost $0 and are never refused for the balance; only Jev decisions are still charged.
AI top-ups
Section titled “AI top-ups”POST /v1/billing/top-up/checkout with { "top_ups": 2 } opens a one-time Checkout for 2 AI top-ups ($20 of AI usage). Each top-up adds $10 of AI usage, dollar for dollar. The card is saved for auto-recharge. 422 top_up_unavailable means the instance offers no AI top-ups.
Auto-recharge
Section titled “Auto-recharge”Off by default. When on, a charged AI call that leaves the balance below threshold_usd buys top_ups AI top-ups with the saved card, at most monthly_limit times a calendar month. The defaults are 1 top-up ($10) when the balance falls below $2, at most 5 times a month.
PATCH /v1/billing/auto-recharge takes:
| Field | Values |
|---|---|
enabled |
true or false |
threshold_usd |
0.01 to 1,000, rounded to cents |
top_ups |
1 to 20 |
monthly_limit |
1 to 30 |
409 payment_method_required: turning it on needs a saved card.POST /v1/billing/payment-methodsaves one.422 recharge_too_small: each recharge must bring the balance back above the threshold, sotop_ups× $10 must be more thanthreshold_usd.- When a payment fails, auto-recharge turns itself off and records
failure. Owners and admins get anauto_recharge_failednotification and an email. Turning it back on clearsfailure.
Owners and admins get an ai_balance_low notification ({ remaining_usd }) when the balance falls below low_at_usd: the threshold, but never more than a fifth of the monthly allowance (at least $0.05). They get ai_balance_out ({ remaining_usd }), and an email, when it falls below $0.01. Each is sent at most once a month, and any top-up or recharge resets both.
Usage report
Section titled “Usage report”GET /v1/billing/usage?from=&to= (RFC 3339 or YYYY-MM-DD; this month by default, at most a year). A YYYY-MM-DD to includes that day.
interface UsageReport { from: string; to: string; // exclusive total_usd: number; total_calls: number; by_feature: { feature: string; usd: number; calls: number }[]; by_day: { date: string; usd: number; calls: number; by_feature: Record<string, number> }[]; // days with usage only; by_feature in US dollars by_member: { user: UserRef | null; usd: number; calls: number }[]; // null: the AI agent and automations by_model: { model: string; usd: number; calls: number; input_tokens: number; output_tokens: number; source: "instance" | "own_key" }[]; own_key_calls: number; // calls on the workspace's own provider key, $0 each}Changes publish a billing.updated event with the billing object.