Tenancy
A tenant is one branded deployment: its own name, logo, theme, enabled venues, feature flags, fee schedule, and users. One platform serves many; the API surface is identical for all of them.
How the tenant is resolved
Section titled “How the tenant is resolved”If you authenticate with an API credential, there is nothing for you to do
here. Your credential is bound to one tenant when it is minted, and that
binding is what scopes every call — the server reads it off your credential row
on every request. No header, Origin or Host can widen it, narrow it, or
contradict it.
# This is the whole thing. No tenant header.curl -s https://api.troncharts.xyz/api/v1/venues \ -H "authorization: Bearer $TOKEN"Calling from a host that belongs to no tenant — api.troncharts.xyz, your own
backend, a Lambda — is the normal case and is accepted. That was not always
true: until 2026-08-17 such a call was refused with
404 {"error":"unknown_tenant_origin"} before your credential was read, which
forced every integration to send X-Tenant-Slug naming a tenant its key already
named. If you have that header wired up, you can leave it or drop it; it changes
nothing.
Cookie callers (browser front ends)
Section titled “Cookie callers (browser front ends)”With no credential there is nothing to read the tenant from, so a browser session resolves in this order:
X-Tenant-Slugheader — trusted only when it names a real tenant; an unknown slug is silently ignored rather than rejected, and resolution falls through. A slug naming a suspended tenant still resolves — it is rejected afterwards with410 {"error":"tenant_suspended"}, not skipped.- Origin / Host — matched against the tenant’s registered origins. This is
how a front end on a branded domain resolves without a header. When no
Originis present theHostheader is used instead. - Platform default — only when the request carries neither
OriginnorHost, or when the origin is the platform tenant’s own registered origin.
Anything else is rejected with 404 {"error":"unknown_tenant_origin"}. The same
404 is what a credential-less or invalid-credential server call still gets:
an expired, revoked or malformed bearer leaves nothing to pin the tenant to, so
the rejection lands exactly where it did before.
A small set of paths skip the gate entirely, because the caller has no tenant to
offer yet: the bearer mint and revoke (/api/auth/api-token,
/api/auth/api-token/revoke), /api/v1/discovery, self-serve signup
(/api/public/tenants/*), the public firm-transparency reads, and the docs
themselves.
What the header does and does not do
Section titled “What the header does and does not do”X-Tenant-Slug decides the tenant for cookie callers only. It does not
choose which tenant your credential acts as, and it cannot widen a credential.
A bearer token acts under its credential’s own tenant, read from the credential
row on every request; the tenant the request resolved to is deliberately never
consulted. So a bearer that reaches a host belonging to another tenant still
acts as its own tenant — the request is not rejected, the header is simply
inert. The one case that is rejected (403 tenant_mismatch) is a token whose
pinned claim disagrees with its credential row, which happens only if the
credential was moved between tenants after the token was minted. Re-mint and it
clears.
A suspended tenant returns 410 {"error":"tenant_suspended"}, checked
against your credential’s tenant — reaching a live tenant’s host does not revive
a suspended key.
What an operator controls per tenant
Section titled “What an operator controls per tenant”| Area | Controls |
|---|---|
| Brand | Display name, logo, favicon, support email |
| Theme | Colour tokens, typography, radius — light and dark |
| Markets | Which venues and symbols are enabled |
| Features | Per-tenant feature flags |
| Fees | The tenant’s fee schedule |
Read the effective configuration back at any time — useful for mirroring it into your own front end:
curl -s https://api.troncharts.xyz/api/v1/tenants/me \ -H "authorization: Bearer $TOKEN" \ -H "x-tenant-slug: $TC_TENANT_SLUG"# → { id, displayName, branding, theme, flags, venues, plan, … }Sub-resources: /api/v1/tenants/{id}/branding, /theme, /flags, /venues,
/plan. These are read-only unless your credential carries tenant:config.
The isolation rule
Section titled “The isolation rule”Tenant isolation is the load-bearing rule of the platform: every read and every write is filtered by the caller’s tenant, at the query, not in the application layer. This holds for REST and for the WebSocket channels — a socket is scoped to its tenant at handshake and is torn down if that access is revoked mid-connection.
The practical consequence for you: you never have to filter by tenant yourself, and you can treat any row you can see as yours.