Authentication
Bearer tokens
In production both services require an OIDC JWT bearer token on every request — there are no anonymous endpoints:
curl https://api.example.com/ledger/v1/balances?… \
-H "Authorization: Bearer <JWT>" \
-H "X-Tenant-ID: org-84120557"
In local development (dev/test profiles) OIDC is disabled and the tenant falls back to a configured development tenant — you can call the API without a token.
Tenant scoping
Send the organization id in the X-Tenant-ID header on every request. If the header is missing or blank, the API responds 400. The token must grant access to that same organization, otherwise the API responds 403.
Permissions
Permissions live in the JWT claim https://sirk.app/permissions and follow the pattern {orgId}:{action}:{resource}:
| Operation | Required permission |
|---|---|
| Reads — balances, lines, subledgers, journal, reports, event inquiry, rule sets, live event stream | {orgId}:read:ledger |
| Writes — record transactions and corrections, submit events and batches, workflow tasks, tax computation | {orgId}:write:ledger |
| Registry reads — list or fetch dimension entities (parties, employees, assets, projects, categories) | {orgId}:read:registry |
| Registry writes — create, update or delete dimension entities | {orgId}:write:registry |
| Filings — submit skattemelding / årsregnskap to Altinn, Skatteetaten or Brønnøysundregistrene | {orgId}:submit:filings |
| Administration — failed-events DLQ (list, read, reprocess, discard), dimension purge, workflow-task deletion, tenant task flags | {orgId}:admin:ledger |
Holding write:ledger implies read:ledger, and write:registry implies read:registry. The ledger and registry namespaces are independent — write:ledger implies neither registry permission — and neither submit:filings nor admin:ledger is implied by anything; they must be granted explicitly.
The organization id is taken from the X-Tenant-ID header (or, if absent, from the token’s https://sirk.app/organizations claim). The token must also list that organization in its https://sirk.app/organizations claim.
Only exact {orgId}:{action}:{resource} grants are honoured — there is no organization-level bridge.
WebSocket authentication
The live event stream at ws://…/ledger/v1/events is open at the HTTP layer; authentication happens in the first message, where the client sends its token as part of the subscribe request. See Journal & event stream.
Failure responses
| Status | Body | Cause |
|---|---|---|
400 | {"error":"missing_tenant", …} | Missing or blank X-Tenant-ID (and no organization in the token to fall back to) |
403 | {"error":"not_a_member", …} | The token’s https://sirk.app/organizations claim does not contain the addressed tenant |
403 | {"error":"missing_permission","detail":"{orgId}:{action}:{resource}"} | Token lacks the required permission — detail names exactly which one |