Currency policy
Kontier does not let you bill in any currency you like. Every workspace carries an
explicit allow-list of currency codes, and a write in a currency that is not on
it is refused with a 400.
The list is empty when a workspace is created. Nothing seeds it — not the organization's default currency, not the plan you are about to sell. Until somebody enables a currency, the workspace accepts none.
Two scopes, one row per currency
A row on the allow-list is a currency code plus two independent flags:
| Flag | What it permits |
|---|---|
billing_enabled | Customers, wallets, subscriptions and credit notes may be denominated in this currency. |
catalog_enabled | Prices and costs may be authored in this currency. |
At least one of the two must be true; a row with both off is refused. The split is
useful in its own right — a currency with catalog_enabled: true and
billing_enabled: false is one you may author prices in but not yet bill anyone
in.
GET /v1/currencies
references is a live count of everything already denominated in that code. It is
what the delete guard consults, and it is worth reading before you change anything.
The list is per workspace
workspace_currencies is scoped to the workspace, not the organization. Live
and sandbox therefore hold completely separate allow-lists, and nothing copies one
into the other.
A flow that passes in sandbox can fail in live
Enabling EUR in your sandbox does nothing for your live
workspace. When you promote an integration, enable the same currencies again on the
live side, or the first customer create fails with CURRENCY_NOT_ALLOWED.
A new workspace starts closed
This is the part that surprises people, so it is worth stating plainly: the allow-list denies by absence. A missing row is not "unknown, allow it" — it is a refusal. A workspace with zero rows rejects every currency in every scope.
That includes the currency the platform would otherwise have defaulted to. On a brand-new workspace this call fails:
POST /v1/customers on a fresh workspace
No currency was named, so the customer would have been created in EUR — and EUR
is not on the allow-list yet, so the request is refused:
Response 400
The field names whatever the caller was writing (preferred_currency, currency,
amount.currency, source_currency), and the message says billing or catalog
depending on which scope was being checked — an FX override, which needs the code
enabled in either scope, names neither. The code is always
CURRENCY_NOT_ALLOWED, and the status is always 400.
Enabling a currency is therefore setup, not tuning. Do it once per workspace, before the first write.
What the allow-list actually gates
It is a gate on writes that choose a currency, and it is checked at the moment of the write:
| What you write | Scope checked | Field named in the error |
|---|---|---|
A customer's preferred_currency (create and update) | billing | preferred_currency |
A subscription's currency (create) | billing | currency |
A wallet's currency (create) | billing | currency |
A credit note's currency (create) | billing | currency |
A price's currency (create and update) | catalog | currency |
A cost's amount.currency (create and update) | catalog | amount.currency |
An FX override's source_currency / target_currency | either | source_currency / target_currency |
Invoicing is not gated by the allow-list
The invoice pipeline never consults it. An active subscription in a currency you later disable keeps producing invoices in that currency, because its denomination was decided when the subscription was created. Disabling a currency stops new customers, wallets, subscriptions and prices from choosing it; it does not stop money that is already denominated in it.
Managing the list
Five endpoints, all implicitly scoped to the workspace your API key is acting in. There is no organization-level or per-workspace-path variant.
| Method and path | What it does |
|---|---|
GET /v1/currencies | The whole list, each row with its references counts. Not paginated — the list is small and bounded. |
GET /v1/currencies/{code} | One row, same shape. 404 when the code is not on the list. |
POST /v1/currencies | Enables a currency. 201 on success, 409 if it is already there. |
PATCH /v1/currencies/{code} | Toggles billing_enabled / catalog_enabled. Nothing else about a row is mutable. |
DELETE /v1/currencies/{code} | Removes the row entirely. 204, or 409 while it is still referenced. |
Enable a currency for both scopes
Codes are upper-cased and trimmed for you, and must be three letters A–Z. That
is a shape check, not a membership check against the ISO 4217 register: a
well-formed code that is not a real currency is accepted, and every amount in it
will then round at two decimals, which is the default scale for anything outside
the registered scale table.
In the dashboard the same list lives under Settings → Currencies — the page the
CURRENCY_NOT_ALLOWED message points at.
Turning a currency off
Two different operations, and the safer-sounding one is the less guarded one.
DELETE is reference-guarded. Before removing a row the API counts, in the same
transaction, everything that still uses the code — customers, wallets, invoices,
credit notes, subscriptions, prices and costs. A non-zero total refuses the delete
with a 409 that says exactly what is in the way:
Response 409
The catalog counts deliberately over-count: a price that only appears on a superseded product version still bills for subscriptions pinned to that version, so it counts as a reference.
PATCH billing_enabled: false is not guarded. It succeeds against a workspace
with thousands of live invoices in that currency, and — as above — those
subscriptions keep invoicing. Read GET /v1/currencies/{code} first and decide what
you mean:
- "stop anyone opening new business in this currency" →
PATCHthe scope off. - "this currency was a mistake and nothing uses it" →
DELETE, and let the guard confirm that nothing does.
Neither operation migrates anything. Moving a customer or a subscription off a currency is a separate, deliberate act — see Changing a customer's billing currency.
Related
- Currencies, amounts and dates — the wire format, scales, and the currency model
- Customers —
preferred_currency, and how to change it - Subscriptions — why the subscription's currency is the contract
- Exchange rates — what happens when the catalog does not price the currency you bill in
- Errors — the problem-document shape and the full error catalog
- Currencies API — the five allow-list endpoints, field by field