iblai-api-billing
Operate the organization's billing, credits, and item-paywall surface from
the API: credit accounts (display balance, auto-recharge preferences, manual
top-up), transaction history, per-item paywall configuration and price tiers,
Stripe-Connect checkout (authenticated + guest), subscriptions, access checks,
public pricing, and platform-wide reporting. Paywalls are generic: every
endpoint keys off item_type + item_id, so the same surface sells agents,
courses, programs, pathways, or any custom item type. Use when reading a credit
balance, enabling a paywall and adding prices, generating a checkout link,
checking whether a user has access, or pulling subscriber/revenue reports.
Auth & conventions
- Base URL:
https://api.iblai.app/dm— these are Data Manager (DM) endpoints, so the/dmprefix is required; the/api/billing/...paths below are appended to it (e.g.https://api.iblai.app/dm/api/billing/account/). Omitting/dmwill not resolve. - Header:
Authorization: Api-Token $IBLAI_API_KEYon every request. - Path vars:
{platform_key}=$IBLAI_ORG(the org key; also calledorg/platform_orgelsewhere on the wire).{item_type}= the item kind (mentor,course,program,pathway, or a custom type — see Notes).{item_id}= the item's identifier (e.g. an agent slug, course id, orconfig/priceUUID for the by-id variants). - DELETE / destructive / outward-facing calls (paywall writes, price create/ update/delete, checkout, subscribe, cancel) say "Confirm with the user first."
- Permission tiers vary by endpoint and are noted inline: authenticated user (own account), platform admin / CanSellItems (paywall config, prices, reporting), and public / no-auth (public pricing, guest checkout, Stripe callback). See Notes.
- Not connected yet? Run
/iblai-api-loginfirst to populateIBLAI_ORG,IBLAI_USERNAME, andIBLAI_API_KEY.
Reads
Credit account & transactions
- GET
/api/billing/account/— current user's credit account info:has_credits,account_id,available_credits, auto-recharge fields, plan info (current_plan,previous_plan,free_trial,can_use_auto_recharge),has_payment_method,is_owner, andpricing_table/messagewhen applicable. Passplatform_keyquery param to read a platform account instead of your own — requires platform admin, a platform API token, orIbl.Billing/Credits/read; plain members get minimal info; non-members are denied. - GET
/api/billing/transactions/— paginated transaction history for the account (only user-facing fields:payment_amount_usd,credits_amount,credits_balance_after,description). Query params:platform_key(omit for own account; read permission enforced),transaction_type(add|subtract|reserve|release|rollover|refund),from_date/to_date(YYYY-MM-DD, inclusive),page,page_size(default 20, max 100).
Paywall config & prices (platform admin / CanSellItems)
- GET
/api/billing/platforms/{platform_key}/items/{item_type}/{item_id}/paywall/— read the item's paywall config; returns a default disabled config when none exists. - GET
…/{item_type}/{item_id}/paywall/prices/— list active price tiers (sorted bysort_order, thenamount). - GET
…/paywall/prices/{price_id}/— get one price by its UUID.
Subscriptions
- GET
…/{item_type}/{item_id}/subscription/— the current user's subscription to this item.404if none. - GET
/api/billing/platforms/{platform_key}/my-subscriptions/— paginated list of the current user's subscriptions on the platform. Query params:status(subscription status — see Notes),item_type,search(matchesitem_id),page,page_size.
Access checks
- GET
/api/billing/access-check/{item_type}/{item_id}/— does the authenticated user have payment access to the item? Returns200with{has_access, item_type, item_id, reason, requires_payment, pricing_available, pricing, subscription}when access is granted, or402Payment Required (same body,has_access:false, withpricing) when payment is needed. Resolves the platform from theplatform_keyquery param or request context. Items with no registered paywall backend grant access by default. - GET
…/{item_type}/{item_id}/access-check/(platform-scoped) — same check, withplatform_keytaken from the path instead of a query param.
Public pricing (no auth)
- GET
…/{item_type}/{item_id}/pricing/— public / no-auth pricing for an item:{item_type, item_id, item_name, is_paywalled, allow_free_tier, trial_period_days, prices[]}. Returns sensible defaults when no paywall config exists. - GET
/api/billing/items/{config_unique_id}/public-pricing/— public / no-auth variant that resolves the item from the paywall config's UUID, then returns the same payload.
Checkout (Stripe Connect)
- GET
…/{item_type}/{item_id}/checkout-callback/{checkout_session_id}/and GET…/{item_type}/{item_id}/checkout-callback/— public / no-auth endpoint Stripe redirects to after checkout. Verifies the session, processes completion (creates/updates the subscription, same path as the webhook), and302-redirects to the return URL. Session id comes from the path or thecheckout_session_id/stripe_checkout_idquery param;return_urlquery param optional. Not called directly — Stripe drives it.
Platform reporting (platform admin / CanSellItems)
- GET
…/{item_type}/{item_id}/subscribers/— paginated subscribers for one item. Query params:status(subscription status),search(matches username / email / item_id),page,page_size. - GET
/api/billing/platforms/{platform_key}/subscribers/— paginated subscribers across all items on the platform. Query params:status,item_type,search,page,page_size. - GET
/api/billing/platforms/{platform_key}/paywalls/— paginated list of all paywall configs on the platform. Query params:item_type,is_enabled(boolean),search(matchesitem_id/description),page,page_size. - GET
/api/billing/platforms/{platform_key}/revenue/— aggregate sales summary across all items:{sales_volume, sales_count, currency}(succeeded payments only).
Writes
Credit account & transactions
- PUT / PATCH
/api/billing/account/— update auto-recharge preferences on your own account (PATCH is identical to PUT). Body fields (all optional):
When enabling, missing values are auto-filled (only-limit → amount = 20% of limit; only-amount → limit = 5× amount; neither → limit 20 / amount 4). Writing to a platform account requires write permission (admin / API token /{ "auto_recharge_enabled": "boolean", "auto_recharge_threshold_usd": "decimal (>= 0)", "auto_recharge_amount_usd": "decimal (min 0.50)", "auto_recharge_spending_limit_usd": "decimal (0 = unlimited)", "platform_key": "string (read-only in this context)" }Ibl.Billing/Credits/write). - POST
/api/billing/auto-recharge/trigger/— run a charge once. Withamount_usd: manual top-up (charge that amount, add credits; no threshold/limit/cooldown). Without it: run auto-recharge once if enabled and below threshold. Returns{status: "triggered"|"skipped"};400if skipped or failed (e.g. no payment method, cooldown, spending-limit exceeded). Confirm with the user first (it charges a card).{ "amount_usd": "decimal (optional)", "platform_key": "string (optional, default 'main')" }
Paywall config & prices (platform admin / CanSellItems)
- POST / PUT
…/{item_type}/{item_id}/paywall/— create or update the paywall config (PUT is identical to POST). Creates a Stripe product on first enable (requires a payment-ready Stripe Connect account). Confirm with the user first. Body (all optional):{ "is_enabled": "boolean", "allow_free_tier": "boolean", "trial_period_days": "integer (>= 0)", "description": "string", "on_successful_payment": "url (<= 500 chars)", "grandfathering_strategy": "free_forever | require_subscription (default require_subscription)", "item_name": "string", "item_metadata": "object" } - DELETE
…/{item_type}/{item_id}/paywall/— disable the paywall (setsis_enabled=False; does not delete the config). Returns204. Confirm with the user first. - POST
…/{item_type}/{item_id}/paywall/prices/— create a price tier. Requires the paywall enabled and a payment-ready Connect account; creates the Stripe price. Returns201. Confirm with the user first.{ "amount": "decimal (>= 0, required)", "currency": "string (default 'usd')", "interval": "month | year | one_time (default month)", "name": "string", "description": "string", "is_active": "boolean", "features": "object/list", "remark": "string", "sort_order": "integer" } - PUT
…/paywall/prices/{price_id}/— update a price (partial). If pricing fields (amount/currency/interval) change and a Stripe price exists, a new Stripe price is created and the old one deactivated. Confirm with the user first. - DELETE
…/paywall/prices/{price_id}/— soft-delete the price tier and deactivate its Stripe price. Returns204. Confirm with the user first.
Checkout (Stripe Connect)
- POST
…/{item_type}/{item_id}/checkout/— create a Stripe checkout session for the authenticated user. Returns{checkout_url, session_id, platform_key}.400if the user already has an active subscription. Requires a payment-ready Connect account and a Stripe- configured price. Confirm with the user first.{ "price_id": "uuid (required)", "success_url": "url", "cancel_url": "url" } - POST
…/{item_type}/{item_id}/checkout-guest/— public / no-auth guest checkout;emailis required. Same response shape. Confirm with the user first.{ "price_id": "uuid (required)", "email": "email (required)", "success_url": "url", "cancel_url": "url" } - POST
/api/billing/prices/{price_unique_id}/checkout-guest/— public / no-auth guest checkout that resolves the platform / item_type / item_id from the price's UUID, then runs the standard guest flow (emailrequired; the price's paywall must be enabled). Confirm with the user first.
Subscriptions
- POST
…/{item_type}/{item_id}/subscription/cancel/— cancel the current user's subscription. For recurring subscriptions returns a Stripe customer- portal URL ({portal_url}); for one-time purchases cancels directly and returns the subscription.404if none. Confirm with the user first.{ "return_url": "url (optional)" }
Credit consumption (sample)
- POST
/api/billing/credits/sample-action/— sample endpoint protected by the@consume_creditsdecorator (authenticated). Checks balance first: if insufficient, returns402with apricing_table; otherwise runs and consumes 1 credit, returning{status, message, ...balance}. This is a reference/test endpoint demonstrating the credit-gating pattern, not a production action.
Example
Enable a paywall on a course-type item and read its public pricing (note the
/dm prefix). Confirm the paywall write with the user first:
curl -X POST \
"https://api.iblai.app/dm/api/billing/platforms/$IBLAI_ORG/items/course/course-v1:ACME+CS101+2024/paywall/" \
-H "Authorization: Api-Token $IBLAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"is_enabled": true, "trial_period_days": 7, "allow_free_tier": true}'
Notes
- item_type values. Built-in types are
mentor,course,program, andpathway. Any other value is normalized to acustom:<slug>namespace (e.g.workshop→custom:workshop); the type must start with a letter after slugification or the request400s. Do not treat this as a agent-only paywall — it is the generic item-paywall surface for any sellable item. - Stripe Connect is required for monetary operations. Enabling a paywall,
creating/updating prices, and checkout all need the platform's Stripe Connect
account to exist and be payment-ready; otherwise you get a
400/validation error. Checkout returns a Stripe-hostedcheckout_url; cancellation of a recurring subscription returns a Stripe customer-portal URL. - 402 Payment Required is meaningful:
GET access-check/...returns402(not403) when the user lacks access and must pay, and the@consume_credits-gated sample endpoint returns402with apricing_tablewhen the balance is too low. - Permission tiers.
- Authenticated user (own data):
account/,auto-recharge/trigger/,transactions/,access-check/...,subscription/,subscription/cancel/,my-subscriptions/,checkout/,credits/sample-action/. Reading/writing another (platform) credit account additionally requires admin / platform API token /Ibl.Billing/Credits/read|write. - Platform admin + CanSellItems: all paywall config + price endpoints and
the platform reporting endpoints (
subscribers/,paywalls/,revenue/). These enforceIsPlatformAdminand theIbl.Billing/CanSellItems/actionRBAC policy (when RBAC is enabled) or a matching platform API key. - Public / no-auth:
pricing/,items/{config_unique_id}/public-pricing/,checkout-guest/(both variants), andcheckout-callback/(Stripe redirect). These explicitly allow unauthenticated access.
- Authenticated user (own data):
- Subscription status values used as the
statusfilter / in responses:active,free,grandfathered,trialing,past_due,canceled,incomplete. - Price
intervalvalues:month,year,one_time.one_timeprices checkout in Stripepaymentmode; recurring prices usesubscriptionmode and honortrial_period_days. - Async / webhooks. Checkout completion is processed both via the
checkout-callback/redirect and via Stripe webhooks (the same handler), so a subscription may be created without your code polling. The callback enriches its return URL withemail,platform_key, andsubscription_id. - Pagination.
transactions/uses page-number pagination (defaultpage_size20, max 100); the reporting/list endpoints (subscribers/, platformsubscribers/,paywalls/,my-subscriptions/) use the standard page-number paginator. Internal USD amounts are never exposed in transaction history — only the user-facing payment/credit fields.