Installs into .claude/skills of the current project.
Are you the author of Iblai Api Billing?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/iblai-iblai-api-billing)
---
name: iblai-api-billing
description: Operate an ibl.ai organization's billing surface via the platform API — credit accounts (balance, auto-recharge, manual top-up), transaction history, generic item paywalls (config + price tiers), Stripe Connect checkout (authenticated and guest), subscriptions, access checks, public pricing, and platform reporting (subscribers, revenue, paywalls). Generic item_type/item_id model: any sellable item (agent, course, program, pathway, or custom). Use when reading credit balances, configuring an item's paywall and prices, generating a checkout link, checking access, or pulling subscriber/revenue reports.
metadata:
kind: api
---
# 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 **`/dm` prefix is required**; the `/api/billing/...` paths
below are appended to it (e.g.
`https://api.iblai.app/dm/api/billing/account/`). Omitting `/dm` will not
resolve.
- **Header:** `Authorization: Api-Token $IBLAI_API_KEY` on every request.
- **Path vars:** `{platform_key}` = `$IBLAI_ORG` (the org key; also called
`org` / `platform_org` elsewhere 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, or
`config`/`price` UUID 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-login`** first to populate `IBLAI_ORG`,
`IBLAI_USERNAME`, and `IBLAI_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`, and
`pricing_table`/`message` when applicable. Pass `platform_key` query param to
read a **platform** account instead of your own — requires platform admin, a
platform API token, or `Ibl.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 by `sort_order`, then `amount`).
- **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. `404` if 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` (matches
`item_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? Returns `200` with
`{has_access, item_type, item_id, reason, requires_payment,
pricing_available, pricing, subscription}` when access is granted, or
**`402` Payment Required** (same body, `has_access:false`, with `pricing`)
when payment is needed. Resolves the platform from the `platform_key` query
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, with `platform_key` taken 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), and
`302`-redirects to the return URL. Session id comes from the path or the
`checkout_session_id`/`stripe_checkout_id` query param; `return_url` query
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` (matches `item_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):
```json
{
"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)"
}
```
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 /
`Ibl.Billing/Credits/write`).
- **POST** `/api/billing/auto-recharge/trigger/` — run a charge once.
With `amount_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"}`; `400` if skipped
or failed (e.g. no payment method, cooldown, spending-limit exceeded).
Confirm with the user first (it charges a card).
```json
{ "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):
```json
{
"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 (sets
`is_enabled=False`; does **not** delete the config). Returns `204`. 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. Returns `201`. Confirm with the user first.
```json
{
"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. Returns `204`. 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}`. `400` if the user already has an
active subscription. Requires a payment-ready Connect account and a Stripe-
configured price. Confirm with the user first.
```json
{ "price_id": "uuid (required)", "success_url": "url", "cancel_url": "url" }
```
- **POST** `…/{item_type}/{item_id}/checkout-guest/` — **public / no-auth**
guest checkout; `email` is required. Same response shape. Confirm with the
user first.
```json
{ "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 (`email`
required; 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. `404` if none. Confirm with the user first.
```json
{ "return_url": "url (optional)" }
```
### Credit consumption (sample)
- **POST** `/api/billing/credits/sample-action/` — sample endpoint protected by
the `@consume_credits` decorator (authenticated). Checks balance first: if
insufficient, returns **`402`** with a `pricing_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:
```bash
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`, and
`pathway`. Any other value is normalized to a `custom:<slug>` namespace
(e.g. `workshop` → `custom:workshop`); the type must start with a letter
after slugification or the request `400`s. 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-hosted `checkout_url`; cancellation of a
recurring subscription returns a Stripe customer-portal URL.
- **402 Payment Required** is meaningful: `GET access-check/...` returns `402`
(not `403`) when the user lacks access and must pay, and the
`@consume_credits`-gated sample endpoint returns `402` with a `pricing_table`
when 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 enforce `IsPlatformAdmin` and the `Ibl.Billing/CanSellItems/action`
RBAC 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), and `checkout-callback/` (Stripe
redirect). These explicitly allow unauthenticated access.
- **Subscription status** values used as the `status` filter / in responses:
`active`, `free`, `grandfathered`, `trialing`, `past_due`, `canceled`,
`incomplete`.
- **Price `interval`** values: `month`, `year`, `one_time`. `one_time` prices
checkout in Stripe `payment` mode; recurring prices use `subscription` mode
and honor `trial_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 with `email`, `platform_key`, and `subscription_id`.
- **Pagination.** `transactions/` uses page-number pagination (default
`page_size` 20, max 100); the reporting/list endpoints (`subscribers/`,
platform `subscribers/`, `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.