Koo Docs
View as Markdown

Billing

The account’s subscription, from Koo’s mirror of Stripe

GET/accounts/{accountId}/subscription
Rolesreadereditoradminowner

Owner-only: it exposes invoices and the card’s brand/last4. Returns 404 while the account is on Free. The `invoices` list is empty until the billing webhook mirrors them.

Parameters
accountIdstringpathrequired
Returns
200 · SubscriptionDto404 · ErrorEnvelopeDto
idstringread-only
accountIdstringread-only
status"active" | "trialing" | "past_due" | "canceled" | "paused" | "unpaid" | "incomplete" | "incomplete_expired"read-only
cadence"monthly" | "annual"required
currentPeriodEndstring <date-time>read-only
cancelAtPeriodEndbooleanread-only
priceVersionstringread-only
legacyUntilstring <date-time>read-only
couponCodestringread-only
couponobjectread-only
itemsobject[]read-only
invoicesobject[]read-only
paymentMethodobjectread-only
cURL
GET /accounts/{accountId}/subscription
bash
curl https://api.koo.io/accounts/:accountId/subscription \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "id": "acct_01example0000000000000000x",  "accountId": "acct_01example0000000000000000x",  "status": "active",  "cadence": "monthly",  "currentPeriodEnd": "2026-01-01T00:00:00.000Z",  "cancelAtPeriodEnd": true,  "priceVersion": "example",  "legacyUntil": "2026-01-01T00:00:00.000Z",  "couponCode": "example",  "coupon": {    "code": "example",    "label": "example",    "percentOff": 1,    "amountOffCents": 0  },  "items": [    {      "kind": "plan",      "ref": "example",      "quantity": 0,      "unitPriceUsd": 0    }  ],  "invoices": [    {      "id": "acct_01example0000000000000000x",      "periodStart": "2026-01-01T00:00:00.000Z",      "periodEnd": "2026-01-01T00:00:00.000Z",      "totalUsd": 0,      "kind": "subscription",      "status": "example",      "pdfUrl": "https://example.com"    }  ],  "paymentMethod": {    "brand": "example",    "last4": "example",    "expMonth": 1,    "expYear": 1  }}

Subscribe to a paid plan — returns a Stripe Checkout URL

POST/accounts/{accountId}/subscription
Rolesreadereditoradminowner

Prices the SUBSCRIPTION as it stands today (plan fee + extra seats) and opens a hosted Stripe Checkout session. Compute — the resources your services run — is not part of Checkout: it bills monthly in arrears for what you actually ran, minus the always-free allowance and your plan credit. **The tier does not change here.** It flips only when Stripe confirms the subscription through the billing webhook, so a client must never show the new plan on this response. Idempotent: a second call reuses the account’s Stripe customer, and a call while a subscription already exists returns 409. `monthly` and `annual` cadences are supported.

Parameters
accountIdstringpathrequired
Request body
tierIdstringrequired

Target tier id — must be a self-serve tier (never `enterprise`).

cadence"monthly" | "annual"

Billing interval. `annual` bills a year of plan + seats up front at the tier’s 10× price (the commitment discount). Compute always bills monthly in arrears for what you ran, either way.

Returns
200 · CheckoutSessionDto403 · ErrorEnvelopeDto409 · ErrorEnvelopeDto502 · ErrorEnvelopeDto
checkoutUrlstring <uri>read-only

Redirect the browser here to complete payment.

cURL
POST /accounts/{accountId}/subscription
bash
curl https://api.koo.io/accounts/:accountId/subscription \  -X POST \  -H "Authorization: Bearer kc_your_api_token" \  -H "Content-Type: application/json" \  -d '{      "tierId": "tier_01example0000000000000000x"    }'
Response
json
{  "checkoutUrl": "https://example.com"}

Cancel the subscription, with the refund the cadence has earned

DELETE/accounts/{accountId}/subscription
Rolesreadereditoradminowner

Owner-only. **Monthly**: the subscription lapses at the end of the period already paid for, with no refund — `accessUntil` is that date. **Annual**: the subscription ends immediately and the unused months are refunded in cash, clawing back the commitment discount (you pay the regular monthly rate for the time you used). A very young annual subscription is cancelled but its refund is held for staff review. The tier reverts to Free when Stripe confirms the cancellation through the billing webhook.

Parameters
accountIdstringpathrequired
Returns
200 · CancelSubscriptionResultDto404 · ErrorEnvelopeDto409 · ErrorEnvelopeDto502 · ErrorEnvelopeDto
status"canceled"read-only
accessUntilstring <date-time> | "now"read-only
refundobjectread-only

Present only when cash was actually refunded.

refundUnderReviewbooleanread-only
cURL
DELETE /accounts/{accountId}/subscription
bash
curl https://api.koo.io/accounts/:accountId/subscription \  -X DELETE \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "status": "canceled",  "accessUntil": "2026-01-01T00:00:00.000Z",  "refund": {    "amountUsd": 0,    "currency": "USD",    "estimatedArrivalDays": 0  },  "refundUnderReview": true}

The account’s compute bill so far this period

GET/accounts/{accountId}/compute-usage
Rolesreadereditoradminowner

Owner-only. Rates the usage ledger LIVE — gross compute accrued this period, the in-kind free allowance applied per service type, the plan’s monthly compute credit (a projection mid-period; Stripe applies it at invoice finalization), and the resulting net charged in arrears at month end. Works for EVERY account including Free (it never 404s); a Free account, or a paid one whose compute item is not yet attached, reports against the current UTC calendar month as a display window. `netUsd` of $0 is a normal answer.

Parameters
accountIdstringpathrequired
Returns
200 · ComputeUsageDto
periodStartstring <date-time>read-only
periodEndstring <date-time>read-only
asOfstring <date-time>read-only
accruedUsdnumberread-only
allowanceobjectread-only
creditobjectread-only

applied/remaining are as-of-asOf projections; the credit is applied at invoice finalization.

netUsdnumberread-only
projectedNetUsdnumberread-only
servicesobject[]read-only
cURL
GET /accounts/{accountId}/compute-usage
bash
curl https://api.koo.io/accounts/:accountId/compute-usage \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "periodStart": "2026-01-01T00:00:00.000Z",  "periodEnd": "2026-01-01T00:00:00.000Z",  "asOf": "2026-01-01T00:00:00.000Z",  "accruedUsd": 0,  "allowance": {    "eligible": true,    "appliedUsd": 0,    "byType": {      "key": {        "appliedUsd": 0,        "coveredServiceId": "coveredservice_01example0000000000000000x"      }    }  },  "credit": {    "includedUsd": 0,    "appliedUsd": 0,    "remainingUsd": 0  },  "netUsd": 0,  "projectedNetUsd": 0,  "services": [    {      "serviceId": "svc_01example0000000000000000x",      "serviceName": "example",      "projectName": "example",      "type": "web",      "accruedUsd": 0,      "allowanceAppliedUsd": 0    }  ]}

Redeem a promo code against the account’s subscription

POST/accounts/{accountId}/coupon
Rolesreadereditoradminowner

Owner-only. Applies a staff-issued promo code to the live subscription; the discount comes off the next invoice and survives service changes and reprices. Requires a paid subscription — a Free account has no invoice to discount (422). An unknown or expired code answers 404/422 without revealing which.

Parameters
accountIdstringpathrequired
Request body
codestringrequired
Returns
204404 · ErrorEnvelopeDto422 · ErrorEnvelopeDto502 · ErrorEnvelopeDto
cURL
POST /accounts/{accountId}/coupon
bash
curl https://api.koo.io/accounts/:accountId/coupon \  -X POST \  -H "Authorization: Bearer kc_your_api_token" \  -H "Content-Type: application/json" \  -d '{      "code": "example"    }'

Remove the applied promo code

DELETE/accounts/{accountId}/coupon
Rolesreadereditoradminowner

Owner-only. The next invoice bills the full amount. Idempotent: removing when none is applied is a no-op.

Parameters
accountIdstringpathrequired
Returns
204404 · ErrorEnvelopeDto502 · ErrorEnvelopeDto
cURL
DELETE /accounts/{accountId}/coupon
bash
curl https://api.koo.io/accounts/:accountId/coupon \  -X DELETE \  -H "Authorization: Bearer kc_your_api_token"

A Stripe Customer Portal session — manage card, invoices, cancellation

GET/accounts/{accountId}/billing-portal
Rolesreadereditoradminowner
Parameters
accountIdstringpathrequired
Returns
200 · BillingPortalDto409 · ErrorEnvelopeDto502 · ErrorEnvelopeDto
urlstring <uri>read-only

Open this to manage payment methods, invoices and cancellation.

cURL
GET /accounts/{accountId}/billing-portal
bash
curl https://api.koo.io/accounts/:accountId/billing-portal \  -H "Authorization: Bearer kc_your_api_token"
Response
json
{  "url": "https://example.com"}