Manage workspace balance, credit top-ups, saved payment methods, and billing settings.
All endpoints require authentication. Use API key scopes billing:read (view balance and settings) and billing:write (checkout, charges, and settings updates).
Transactional emails (balance alerts, invoices, subscription notices) link to team billing pages under /settings/team/billing/.... To manage product email preferences, use Settings → Notifications (welcome and onboarding emails include an unsubscribe link to this page).
→ Subscription — plan signup and Stripe subscription management
Workspace settings URLs
In the web app, settings opens as a dialog over whichever page you're on. The active page is addressed by the URL fragment (#settings/team/billing, #settings/team/keys, …). Navigating inside settings changes only the fragment.
Use path-style entry URLs in docs, emails, and success_url / cancel_url fields: https://app.inference.sh/settings/team/billing?session_id={CHECKOUT_SESSION_ID}. The app redirects to https://app.inference.sh/?session_id=…#settings/team/billing, reads one-time query flags (session_id, setup_session_id, success=1, addon_success=1), then removes them from the URL while keeping the fragment. Legacy /settings/billing and /settings/keys shortcuts redirect to the team paths.
Checkout started in the workspace (top-up, subscription, add-on, saved card setup) returns to the page where checkout began: success adds the flag to that page's query and opens settings on the handler page; cancel returns to the exact starting URL (fragment included). The workspace always passes its own redirect URLs — API defaults such as {origin}/settings/team/billing/subscription?success=1 still work through the /settings/<path> entry redirect.
Organization billing: When the selected team is billed by an organization, credit top-ups in the web app call POST /billing/checkout and POST /billing/charge as the paying team (the organization workspace) when the signed-in user may manage that workspace's billing. Success redirects target that payer's billing settings (#settings/team/billing). Users who can see member-team usage but cannot manage organization billing do not get a checkout session from these flows. See Organizations — Billing.
On the API, write endpoints in this document and in Subscription require the manage_billing capability: team owner on a standalone team, or team owner on the org workspace when the org pays. Calling checkout, charge, subscription, or payment-method routes with an org member team in context returns 403, even if you are an org admin on that member team — switch X-Team-ID (or your SDK team context) to the organization workspace (its team id matches org_id on member teams). Read endpoints (GET /billing, balance, settings, payment history) still work for member-team admins.
Money units
| Context | Unit | Example |
|---|---|---|
GET /billing, GET /billing/balance | Microcents (1 USD = 100,000,000) | 250000000 = $2.50 |
POST /billing/checkout, POST /billing/charge body amount | Cents (1 USD = 100) | 500 = $5.00 minimum top-up |
Get billing account
GET /billing
Returns the workspace's billing account with cached balance.
Response
| Field | Type | Description |
|---|---|---|
balance | int64 | Current balance in microcents |
currency | string | Currency code (for example usd) |
status | string | Account status: active, spend_locked, manual_hold or suspended (see below) |
status | Meaning |
|---|---|
active | Spending is allowed. |
spend_locked | A charge took the balance below the spend floor (-credit_limit). Runs are refused, free runs included. It clears itself when a credit (top-up, auto-recharge, voucher) takes the balance above the floor. |
manual_hold | Held by the platform, for example after a payment dispute. Runs are refused until support lifts the hold. |
suspended | Suspended for a payment, risk or compliance issue. Runs are refused until support resolves it. |
Example:
1curl https://api.inference.sh/billing \2 -H "Authorization: Bearer inf_your_key"Get balance
GET /billing/balance
Recalculates balance from grants and usage, then returns the current value. Use this after checkout when status is processing, or when you need an authoritative balance.
Response
1{2 "balance": 2500000003}balance is in microcents.
Service fee
GET /billing/service-fee
Returns the active service-fee configuration used for top-ups (default 5% + $0.30). Call before checkout or a saved-card charge to show users the full total including fee and tax.
Add credits
Two paths depending on whether a card is already on file.
| Method | Endpoint | When to use |
|---|---|---|
| Stripe Checkout | POST /billing/checkout | No saved card, user picks a payment method, or delayed methods (ACH, SEPA) |
| Saved card | POST /billing/charge | GET /billing/settings reports has_payment_method: true |
Both paths apply the same service fee and tax rules, then credit balance on success.
Stripe Checkout
POST /billing/checkout — requires billing:write.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
amount | int64 | Yes | Credit to add, in cents (minimum 500 = $5) |
success_url | string | Yes | Redirect URL after payment |
cancel_url | string | Yes | Redirect URL if the user cancels |
Response — PaymentRecordDTO with status: "pending" and session_url for redirect. Line items include credit_amount, service_fee, and tax fields filled when checkout completes.
Complete checkout:
POST /billing/checkout/success — body: { "session_id": "<stripe_checkout_session_id>" }.
status | Meaning |
|---|---|
complete | Balance credited now; grant included |
processing | Delayed payment; balance credited when Stripe confirms (poll GET /billing/balance) |
Credits apply exactly once — the success URL and Stripe webhooks share the same completion path.
Example:
1curl -X POST https://api.inference.sh/billing/checkout \2 -H "Authorization: Bearer inf_your_key" \3 -H "Content-Type: application/json" \4 -d '{5 "amount": 2000,6 "success_url": "https://app.inference.sh/settings/team/billing?success=1",7 "cancel_url": "https://app.inference.sh/settings/team/billing"8 }'Saved card charge
POST /billing/charge — requires billing:write.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
amount | int64 | Yes | Credit to add, in cents (minimum 500) |
Success response:
1{2 "status": "complete",3 "grant": { }4}Balance is credited immediately.
Errors (400, code charge_failed):
| Message | Cause |
|---|---|
no saved payment method | Use checkout or save a card first |
minimum top-up amount is $5 | amount < 500 |
payment failed: ... | Stripe declined the charge |
Redeem voucher code
Redeem a promotional or gift code to add credits to the current workspace. In the web app, open settings → workspace → billing and use the Redeem code section (monospace input, case-normalized as you type).
POST /billing/vouchers/redeem — requires billing:write.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Voucher code (whitespace trimmed; matching is case-insensitive) |
Response — CreditGrantDTO for the credited amount. amount and remaining are in microcents. Grant type is typically voucher; notes includes the code string.
Example:
1curl -X POST https://api.inference.sh/billing/vouchers/redeem \2 -H "Authorization: Bearer inf_your_key" \3 -H "Content-Type: application/json" \4 -d '{"code": "PROMO-ABCD-EFGH"}'After a successful redemption, poll GET /billing/balance or call GET /credit-grants/active (requires billing:read) to confirm the updated balance and grant expiry.
Rate limits
| Limit | Window |
|---|---|
| 10 attempts per user | 1 minute |
| 30 attempts per IP | 1 minute |
When exceeded, the API returns 429 with code rate_limited and message Too many redemption attempts. Please try again later.
Errors
Failed redemptions return 400 with code redeem_failed and one of these messages:
| Message | Cause |
|---|---|
invalid code | Code not found |
this code is no longer active | Code revoked or already used (single-use codes) |
this voucher is no longer active | Voucher paused, expired, or exhausted |
this code is not yet valid | Before the voucher valid_from time |
this code has expired | After the voucher valid_until time |
this voucher has reached its maximum number of redemptions | Campaign cap reached |
this code has reached its maximum number of uses | Per-code use limit reached |
you have already redeemed this voucher | Per-user limit reached |
your team has already redeemed this voucher | Per-workspace limit reached |
this code is assigned to a different account | Code is email-restricted |
this voucher is only available for new users | The paying workspace already has a topup or auto_recharge credit grant (for organization billing, that is the org workspace—not the member team in X-Team-ID) |
failed to credit account | Redemption recorded but grant creation failed (retry may be idempotent) |
Voucher administration (creating campaigns, generating codes, pausing vouchers) is internal and not exposed on the public REST API.
Bounty rewards
Credits from bounty programs (for example, the app-builder program via belt bounty claim app-builder, form programs via belt bounty claim <program>, or survey answers via belt feedback) are granted as CreditGrantDTO entries with type bounty or survey. Grants expire after the program's expiry_days (typically 30 days). See Bounties and CLI setup — App bounties.
Credit grants
Prepaid balance is stored as credit grants (top-ups, vouchers, bounties, plan credits, and similar). Usage debits draw from active grants in FIFO order. Grant amount and remaining are in microcents.
| Endpoint | Scope | Description |
|---|---|---|
GET /credit-grants/active | billing:read | Active grants with remaining > 0 |
GET /credit-grants/breakdown | billing:read | Totals by grant type, plus grants expiring within 30 days |
GET /credit-grants or POST /credit-grants/list | billing:read | Paginated grant history (cursor) |
All three also need the view_billing capability on the workspace (admins and owners); plain members get 403 team_role_required.
Organization billing: Grants are stored on the payer workspace (the organization workspace when the org pays). Top-ups, voucher redemptions, and bounty claims made while X-Team-ID is a member workspace still credit the org's grants and debits allocate against those grants. New-user-only voucher codes use the same payer when deciding whether a prior top-up exists. If you administer a member team but cannot see the organization's billing, GET /credit-grants/active and GET /credit-grants/breakdown return empty results (same visibility rule as GET /billing balance for delegated billing). Use the organization workspace id as team context, or see Organizations — Billing.
Billing settings
GET /billing/settings — requires billing:read.
Returns auto-recharge, alerts, invoice fields, and saved-card metadata.
| Field | Description |
|---|---|
has_payment_method | Whether instant top-up (POST /billing/charge) is available |
payment_method_label | Display label (for example Visa •••• 4242) |
auto_recharge_enabled | Auto top-up when balance drops below threshold |
spending_limit | Workspace spending cap (microcents) |
low_balance_threshold | Alert threshold (microcents) |
tax_id | Business tax identifier for invoices |
tax_id_type | Kind of tax_id as selected in the app (lowercase Stripe-style code such as us_ein, de_stn, eu_vat, or other; empty when unset) |
POST /billing/settings — requires billing:write. Send only fields to update (BillingSettingsUpdateRequest). Set tax_id_type to "none" or "" to clear it.
Validation errors (400, code validation_error):
| Message (examples) | Cause |
|---|---|
low balance threshold must be at least $... | low_balance_threshold below the platform minimum |
auto-recharge amount must be at least $... | auto_recharge_amount too small |
auto-recharge threshold must be at least $... | auto_recharge_threshold too small |
invalid tax_id_type "..." | tax_id_type is not a supported lowercase Stripe-style code |
Org-billed workspaces share the payer's settings row (credit limit, thresholds, auto-recharge, and Stripe customer), not a separate member-team copy.
Saved payment methods
| Endpoint | Method | Scope | Purpose |
|---|---|---|---|
/billing/payment-method/setup | POST | billing:write | Stripe Checkout to save a card |
/billing/payment-method/success | POST | billing:write | Complete setup (session_id in body) |
/billing/payment-method | DELETE | billing:write | Remove saved card |
Setup body: { "success_url": "...", "cancel_url": "..." }. Success body: { "session_id": "..." } — returns last4 and brand.
Users can also save a card during a balance checkout when Stripe shows “Save my info for future purchases.”
Payment history
GET /billing/payments or POST /billing/payments/list: needs billing:read and the view_billing capability on the workspace (admins and owners).
Cursor-paginated PaymentRecordDTO rows for the workspace (checkout sessions, saved-card and subscription charges, and their status), with linked invoice/invoices when present. Use the cursor pagination parameters in the query on GET or in the JSON body on POST …/list.
Without a status filter the list returns only complete (1) and processing (4) records. Add a status filter to see pending, failed, expired, refunded or disputed ones.
1curl -X POST https://api.inference.sh/billing/payments/list \2 -H "Authorization: Bearer inf_your_key" \3 -H "Content-Type: application/json" \4 -d '{"limit": 20}'Organization billing: payment records are rows on the payer ledger. A member workspace sees its own records. The organization workspace sees its member workspaces' records from the day org billing opened.
Insufficient balance
Before checking balance, the spend gate evaluates the usage policy for the run (for example the app on POST /run). A policy-blocked run returns 403 with blocked_by_usage_policy, not a paywall — adding credits cannot unblock it until an admin widens the policy.
When the policy allows the run but balance is too low, the API returns 402 Payment Required (payment_required) with a message pointing to settings → workspace → billing. This is separate from plan limit errors (limit_exceeded, feature_not_available) enforced by entitlements.
The belt and infsh CLIs surface billing URLs in error text for 402 responses.
Free runs
A run that is certain to cost $0 does not need credits. When the balance would refuse the run, the API admits it anyway if the workspace has a saved payment method and its billing status is active. Without a saved payment method the API returns 402 with code payment_method_required:
1{2 "type": "https://api.inference.sh/errors/payment_method_required",3 "title": "Payment Required",4 "status": 402,5 "detail": "add a payment method to your workspace's billing settings to run free apps",6 "meta": { "billing_page": "/settings/team/billing/payments" }7}Save a card under settings → workspace → billing → payments (or with POST /billing/payment-method/setup) and retry. Nothing is charged to the card for a free run, and a free run places no pre-auth hold.
A workspace whose spending is locked, that has a manual hold, or that is suspended is refused free runs too, with payment_required and the same message as a paid run.
A run counts as free only when the app version's reviewed pricing settles at $0 for the input and function sent, whatever the app outputs and however long it runs. Every fee expression and the total must read only the input and the function, and the total must be $0 under every fee configuration. A version without reviewed pricing has no app fees, and its run pays for compute: nothing on your own engine, and resource time on inference.sh cloud. Such a run counts as free only with infra: private; cloud and private_first follow the paid path. A $0 estimate is not enough: pricing that reads output, run time or resource_fee is not certain, and neither is a flow app (each of its steps is admitted and held on its own). Those runs follow the paid path.
Paid runs are unchanged: they need a balance above the spend floor, and the run's pre-auth hold reserves its estimated cost.
Related
- Usage API: workspace spend breakdown, usage summary, and CEL pricing preview
- Subscription API — plans and recurring billing
- Usage API — aggregated usage summary and cost breakdown
- Entitlements API — plan limits and
GET /entitlements/usage - Tasks API — usage-based app pricing reads
output_metainternally - Extend pricing — CEL formulas for store apps