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.
Secret keys
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.
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.
| Resource | Scopes |
|---|---|
| Customers | customers:read, customers:write |
| Products | products:read, products:write |
| Prices | prices:read, prices:write |
| Subscriptions | subscriptions:read, subscriptions:write, subscriptions:cancel |
| Usage records | usage_records:read, usage_records:write |
| Checkout | checkout_sessions:read, checkout_sessions:write |
| Payments | payments:read |
| Refunds | refunds:read, refunds:write |
| Disputes | disputes:read (read only, chargebacks come from the provider) |
| Invoices | invoices:read, invoices:write, credit_notes:read |
| Coupons | coupons:read, coupons:write |
| Tax rates | tax_rates:read, tax_rates:write |
| Events | events:read |
| Webhook endpoints | webhook_endpoints:read, webhook_endpoints:write |
| API keys | api_keys:read, api_keys:write |
| Audit logs | audit_logs:read |
| Metrics | metrics:read |
| Tenant | tenant:read, tenant:write |
| Test clocks | test_clocks:read, test_clocks:write |
| Data export | data: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.
| Credential | What it is for |
|---|---|
| client_secret | Returned 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.
BillKit