Billing API Endpoints
REST API endpoints for managing credits and subscriptions. All authenticated endpoints use the auth token to identify the user — no userId parameter is needed.
Get Balance Status
Get current Dollarinos balance and subscription info.
GET /api/v1/billing/balance
Example
curl https://clanker.net/api/v1/billing/balance \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
{
"balance": 450,
"totalSpent": 50,
"canExecute": true,
"isAdmin": false
}
Response Fields
| Field | Type | Description |
|---|---|---|
balance | number | Total available Dollarinos |
totalSpent | number | Total Ds spent all-time |
canExecute | boolean | Whether user has sufficient credits to execute |
isAdmin | boolean | Whether user is an admin |
List Credit Packs
Get available credit packs for purchase.
GET /api/v1/billing/packs
Example
curl https://clanker.net/api/v1/billing/packs \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
{
"packs": [
{ "id": "pack_100", "name": "100 Ds", "dollarinos": 100, "priceCents": 100 },
{ "id": "pack_250", "name": "250 Ds", "dollarinos": 250, "priceCents": 225 },
{ "id": "pack_500", "name": "500 Ds", "dollarinos": 500, "priceCents": 425 },
{ "id": "pack_1000", "name": "1000 Ds", "dollarinos": 1000, "priceCents": 800 },
{ "id": "pack_5000", "name": "5000 Ds", "dollarinos": 5000, "priceCents": 3750 }
]
}
Purchase Credits
Purchase a credit pack.
POST /api/v1/billing/purchase
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
packId | string | Yes | Pack ID to purchase |
email | string | Yes | User email for receipt |
successUrl | string | Yes | Redirect URL on success |
cancelUrl | string | Yes | Redirect URL on cancel |
Example
curl -X POST https://clanker.net/api/v1/billing/purchase \
-H "x-auth-token: YOUR_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"packId": "pack_500",
"email": "[email protected]",
"successUrl": "https://clanker.net/payment-success",
"cancelUrl": "https://clanker.net/payment-cancel"
}'
Response
{
"checkoutUrl": "https://checkout.example.com/c/pay/cs_xxxxxxxxxxxx"
}
Errors
| Status | error.code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid credentials |
| 400 | INVALID_INPUT | Unknown packId, or checkout could not be created |
| 500 | INTERNAL_ERROR | Checkout session creation failed unexpectedly |
Everything downstream of the redirect — card decline, abandonment, refunds — is
settled by the payment provider and reported back through the webhook, not by
this call. A checkoutUrl means the session was created, nothing more.
Return URLs
successUrl and cancelUrl are where the payment provider sends the browser
when checkout finishes. Which pair you send depends on where checkout was
opened from:
| Client | successUrl / cancelUrl |
|---|---|
| Web | https://clanker.net/payment-success / https://clanker.net/payment-cancel — app screens served by the SPA |
| Native (iOS/Android) | https://clanker.net/api/v1/billing/return/success / https://clanker.net/api/v1/billing/return/cancel |
The example above is the web pair: the browser is already inside the app, so the provider can redirect straight to a screen.
Native cannot use those — the checkout runs in a system browser with no route
back into the app. It sends the provider to /api/v1/billing/return/:outcome
instead, where :outcome is success or cancel. That endpoint needs no auth
and returns an HTML page that redirects to the net.clanker://payment-success
(or …/payment-cancel) deep link, with a tappable fallback link for browsers
that block the automatic hop.
Neither page settles the payment. Credits are granted by the webhook, so a user who closes the browser before the redirect still gets what they paid for.
Get Transaction History
Get credit transaction history.
GET /api/v1/billing/transactions
Example
curl https://clanker.net/api/v1/billing/transactions \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
Returns the 50 most recent transactions, newest first. There is no pagination parameter.
{
"transactions": [
{
"id": 1,
"userId": "3ab91c22-…",
"amount": -45,
"type": "usage",
"description": "README Generator execution",
"executionId": "b71e0c94-…",
"stripeSessionId": null,
"createdAt": "2024-01-15T10:31:30.000Z"
},
{
"id": 2,
"userId": "3ab91c22-…",
"amount": 500,
"type": "purchase",
"description": "500 Ds",
"executionId": null,
"stripeSessionId": "cs_xxxxxxxxxxxx",
"createdAt": "2024-01-10T14:00:00.000Z"
}
]
}
A row is a delta, not a balance snapshot — amount is signed and there is
no running-total field. Read the current balance from
GET /api/v1/billing/balance.
Transaction Types
| Type | Description |
|---|---|
usage | Ds spent on a run (negative amount, usually linked by executionId) |
purchase | Ds added — a credit pack, or a Based Mode activation/renewal grant |
Get Subscription Tier
Get current subscription mode.
GET /api/v1/billing/subscription/tier
Example
curl https://clanker.net/api/v1/billing/subscription/tier \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
{
"tier": "paygmode",
"info": {
"name": "PAYG MODE",
"priceCents": 0,
"priceDisplay": "Free",
"monthlyCredits": 0,
"packDiscount": 0
},
"subscriptionExpiresAt": null,
"isTestMode": false
}
Modes
| Mode | Price | Monthly Ds | Pack Discount |
|---|---|---|---|
byokmode | Free | 0 | — |
paygmode | Free | 0 | — |
basedmode | $29/mo | 2,000 | 20% |
sitelicense | Licensed | 0 | — |
sitelicense can come back from this endpoint but is deliberately absent from
/subscription/info, which lists only the three self-serve modes.
Tier derivation: the tier is computed at runtime — not stored as a column. A valid
subscriptionExpiresAtin the future →basedmode. A stored BYOK key →byokmode. Otherwise →paygmode. There is no public endpoint to set the mode directly; it follows from your BYOK key and subscription state.
Get Subscription Info
Get pricing and details for each billing mode.
GET /api/v1/billing/subscription/info
Example
curl https://clanker.net/api/v1/billing/subscription/info \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
{
"modes": {
"byokmode": { "name": "BYOK MODE", "priceCents": 0, "priceDisplay": "Free", "monthlyCredits": 0, "packDiscount": 0 },
"paygmode": { "name": "PAYG MODE", "priceCents": 0, "priceDisplay": "Free", "monthlyCredits": 0, "packDiscount": 0 },
"basedmode": { "name": "BASED MODE", "priceCents": 2900, "priceDisplay": "$29/mo", "monthlyCredits": 2000, "packDiscount": 0.2 }
}
}
BYOK Key Management
Manage your Bring-Your-Own-Key (BYOK) LLM API key. Available on every mode — a
key is the only way to reach a provider this deployment holds no key for. What
a key changes is billing (0 Ds charged for calls that ride on it) and which
provider your requests are routed to; storing one promotes paygmode to
byokmode.
Get BYOK Status
GET /api/v1/byok
Example
curl https://clanker.net/api/v1/byok \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
{
"hasKey": true,
"keyPreview": "sk-...xxxx",
"provider": "anthropic"
}
provider is null when no key is stored.
Set BYOK Key
Store or replace your LLM API key. Switches the account to byokmode.
PUT /api/v1/byok
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | LLM API key. sk-ant-… → Anthropic, any other sk-… → OpenAI |
The provider is decided by the key’s prefix, not by anything you send — the
sk-ant- test runs first, because Anthropic keys also start with sk-.
Example
curl -X PUT https://clanker.net/api/v1/byok \
-H "x-auth-token: YOUR_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "key": "sk-ant-..." }'
Response
{
"hasKey": true,
"keyPreview": "sk-...xxxx",
"provider": "anthropic"
}
Errors
| Status | error.code | Description |
|---|---|---|
| 400 | INVALID_INPUT | key missing, not a string, or not a recognised sk- prefix |
| 404 | NOT_FOUND | User record not found |
Delete BYOK Key
Remove the stored BYOK key. Account reverts to paygmode.
DELETE /api/v1/byok
Example
curl -X DELETE https://clanker.net/api/v1/byok \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
{ "hasKey": false, "keyPreview": null }
Get Publishable Key
Get the publishable key for client-side payment forms.
GET /api/v1/billing/publishable-key
Example
curl https://clanker.net/api/v1/billing/publishable-key \
-H "x-auth-token: YOUR_AUTH_TOKEN"
Response
{
"publishableKey": "pk_live_xxxxxxxxxxxx"
}