BillKit/Docs Console
Get started

Authentication

One credential type reaches the API: a secret key sent as a bearer token. The key also decides whether you are in live or test mode.

Updated Sep 13, 2026 · API version 2026-09-13

Secret keys

shell
curl https://api.billkit.eu/v1/customers \
-H "Authorization: Bearer $BILLKIT_API_KEY"

There are two key forms and no others. bk_live_... operates on live data and moves real money. bk_test_... operates on test data. There is no publishable key and no restricted-key prefix: narrowing a key is done with scopes, described below, and the key still starts with sk_.

The mode is derived from the prefix and verified against the stored key record, so a key cannot be used against the wrong mode even if the record were tampered with.

A malformed header gets 401 invalid_api_key; a missing one gets 401 authentication_required; a key you revoked gets 401 revoked_api_key. All three use the standard error envelope.

A secret key is a full credential for your billing data. Keep it server-side. Nothing in a browser or a mobile binary should ever hold one.

Creating and revoking keys

Manage keys in the console under Developers → API keys, or over the API. The plaintext key is returned in the secret field exactly once, on the create call. After that only the prefix (the first 16 characters) is readable, because BillKit stores a peppered hash rather than the key.

shell
curl https://api.billkit.eu/v1/api_keys \
-H "Authorization: Bearer $BILLKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"label": "reporting pipeline", "scopes": ["customers:read", "subscriptions:read", "invoices:read"]}'

Rotation is manual and deliberately so: create the replacement, deploy it, then POST /v1/api_keys/{api_key_id}/revoke the old one. Both keys work in the overlap, so there is no window where a deploy is racing an expiry. Keys do not expire on their own.

Scopes

Supply a list and the key is refused on any route outside it with 403 insufficient_scope. Every route maps to exactly one scope.

Omit scopes and the new key inherits the scopes of the key that created it. From the console, or from a ["*"] key, that is full access. From a narrowed key it is that narrower set, because delegation may narrow and may never widen: a key holding only api_keys:write cannot mint itself a ["*"] key. Asking for a scope the calling key does not hold is a 400, and so is a scope name that does not exist, so a typo fails at creation rather than producing a key that 403s on every route a week later.

ResourceScopes
Customerscustomers:read, customers:write
Productsproducts:read, products:write
Pricesprices:read, prices:write
Subscriptionssubscriptions:read, subscriptions:write, subscriptions:cancel
Usage recordsusage_records:read, usage_records:write
Checkoutcheckout_sessions:read, checkout_sessions:write
Paymentspayments:read
Refundsrefunds:read, refunds:write
Disputesdisputes:read (read only, chargebacks come from the provider)
Invoicesinvoices:read, invoices:write, credit_notes:read
Couponscoupons:read, coupons:write
Tax ratestax_rates:read, tax_rates:write
Eventsevents:read
Webhook endpointswebhook_endpoints:read, webhook_endpoints:write
API keysapi_keys:read, api_keys:write
Audit logsaudit_logs:read
Metricsmetrics:read
Tenanttenant:read, tenant:write
Test clockstest_clocks:read, test_clocks:write
Data exportdata:export

subscriptions:cancel is separate from subscriptions:write because cancellation is the one subscription write a support tool usually should not be able to make by accident. data:export is separate because GET /v1/tenant/export streams every customer’s personal data and payment history in one download, so a general read-only key should not carry it.

Credentials that are not API keys

Two other credentials appear in BillKit, both short-lived, both scoped to a single subject, and neither usable against /v1 data routes.

CredentialWhat it is for
client_secretReturned by POST /v1/checkout/sessions when ui_mode is embedded. Authorises one checkout session’s element and confirm calls, and nothing else. Safe to hand to the browser. See Embed Checkout.
bk_portal_…A customer portal session token, minted by POST /v1/billing_portal/sessions from a subscription_id. Scoped to that one subscription and short-lived; the token is returned once, and you redirect or email the customer with it.

Pre-authentication throttling

Credential checks are rate limited per client address and route before the key is looked up, so a stolen-key spray costs the attacker budget rather than costing you database work. A successful authentication refunds the token it consumed, which means legitimate traffic never sees it. See Rate limits.

Environment variables

The official SDKs read BILLKIT_API_KEY when no key is passed explicitly. The billkit CLI does not read that variable; it stores credentials in ~/.billkit/config.json after billkit login.