client_id and client_secret issued to your organization. Keep the secret server-side.
Most integrations use the Client Credentials flow for server-to-server access against your own organization’s data. If your app instead acts on behalf of a customer (for example, a HEMS provider reading prices for a specific subscription), use the Authorization Code flow.
Client Credentials Grant
Server-to-server flow against your own organization’s data (RFC 6749 §4.4). No customer interaction is involved.Request a token
/oauth/token responds with:
scope field is a space-delimited list of scopes granted on the token, mirroring the standard OAuth 2.0 scope claim. See Scopes for what each value allows.
Use the access token
Send it in theAuthorization header on every API request:
Refresh
Access tokens last 60 minutes. When yours expires, exchange the refresh token at/oauth/token for a new pair:
Authorization Code Grant
If your app acts on a customer’s behalf instead of running server-to-server, use this flow (RFC 6749 §4.1). The token response, header usage, and refresh model are identical to Client Credentials; only the way you obtain the initial token differs. Your callback URI must be registered against yourclient_id; subpaths under the registered origin are accepted.
Redirect the customer to /oauth/authorize
state is a per-request opaque value you generate, store, and verify on callback (CSRF protection).While the redirect is open, the customer signs in and grants consent on Nomos-hosted screens. See the third-party login guide for the customer journey.
Handle the callback
Nomos redirects with a single-use code (10-minute TTL) and your
state. Reject the callback if state doesn’t match.PKCE
PKCE (Proof Key for Code Exchange) hardens the flow against code interception (RFC 7636). PreferS256 over plain.
Generate a verifier and challenge
Pick a random
code_verifier of 43–128 characters from [A-Za-z0-9-._~].
With S256, the code_challenge is the base64url-encoded SHA-256 of the
verifier; with plain, it’s the verifier itself.Send the challenge on /oauth/authorize
Add
code_challenge and code_challenge_method to the authorize URL.Scopes
Each API key is issued with one or more scopes. Two scopes are defined today:| Scope | Allows |
|---|---|
read:* | All GET requests across the API. |
write:* | All non-GET requests (POST, PATCH, PUT, DELETE). |
read:* write:*) is full-access, the default. A read-only key carries only read:* and is rejected with 403 FORBIDDEN on any mutating request. Choose read-only when issuing a key for a programmatic agent, ETL job, or BI consumer that should never mutate state.
Scopes are set when the key is created and cannot be changed later. To switch a key from full access to read-only (or vice versa), create a new key and rotate.
The scope field is * because future iterations may introduce resource-named scopes (read:invoice, write:subscription) that narrow access further. Keys with the wildcard scopes continue to behave as supersets.
Auth failures return
401 UNAUTHORIZED with the standard error envelope.
Scope failures return 403 FORBIDDEN. See
Errors for the response shape and
common causes.