API Clients
API Clients let external services exchange an organization-scoped OIDC access token for a Rhesis JWT using RFC 8693 token exchange. Use them for machine-to-machine integrations that should authenticate through your identity provider instead of a human API token.
API Clients are an Enterprise Edition feature and require SSO to be configured for the organization.
How token exchange works
- An organization admin creates an API Client in Rhesis.
- Rhesis returns a plaintext
client_secretexactly once. - The external service obtains an OIDC access token from the organization’s IdP.
- The service calls
POST /auth/token-exchangewith RFC 8693 form fields. - Rhesis validates the subject token against the organization’s SSO issuer and returns a Rhesis access token.
The exchange audience binds the request to one organization:
The slug after rhesis:org: must match the organization’s slug.
An exchange can also name the project the resulting token should be scoped to, using the RFC 8693 resource parameter:
The requesting user must be a member of that project. Omitting resource yields a token that can only see organization-level rows (project_id IS NULL); it cannot see anything scoped to a specific project.
Create an API Client
Create clients with the API:
Request fields:
| Field | Required | Description |
|---|---|---|
client_id | Yes | Public identifier matching ^[a-z0-9][a-z0-9_-]{2,63}$ |
name | No | Human-readable label for the admin UI |
expected_subject_azp | Yes | Required azp claim on the subject token |
expected_subject_audience | Yes | Required aud claim on the subject token |
allowed_scopes | Yes | Supported values: read, full, offline_access |
default_scope | Yes | Single scope applied when token exchange omits scope |
The plaintext client_secret is returned only on create and rotate. Copy it immediately and store it in your secret manager.
Exchange a token
Call POST /auth/token-exchange with application/x-www-form-urlencoded. Client credentials can be sent with HTTP Basic authentication or in the form body, but not both.
The resulting access token behaves like a project-scoped rh-* API key: every request it authenticates is scoped to that one project, the same way X-Project-Id scopes a browser session. There’s no per-request override — a service that needs to work across several projects exchanges once per project. Project membership is re-checked on every request, so removing the exchanged user from the project cuts off access immediately, without waiting for the token to expire.
Successful responses follow the OAuth token response shape:
refresh_token is present only when the resolved scope includes offline_access. Both lifetimes are deployment-configurable (JWT_ACCESS_TOKEN_EXPIRE_MINUTES and JWT_REFRESH_TOKEN_EXPIRE_DAYS); the values above are the defaults.
Discover available projects
resource needs a project UUID, and project UUIDs are not visible anywhere in the IdP or in the client configuration. To find one, exchange once with no resource and use the resulting token to list the projects the subject user belongs to:
GET /projects/ returns every project the org member is a member of, org-scoped rather than project-scoped, so the token from step 1 works for it without a resource. Pick an id from the list and pass it as the resource in a second exchange to get a project-scoped token.
The Java SDK exposes the same call as client.projects().list() — point RhesisClientBuilder.apiKey(...) at the org-scoped token from step 1, list projects, then re-point it at the project-scoped token from the second exchange.
Manage clients
| Endpoint | Purpose |
|---|---|
POST /organizations/{org_id}/auth-clients | Create a client and return one-shot secret |
GET /organizations/{org_id}/auth-clients | List clients without secrets |
GET /organizations/{org_id}/auth-clients/{id} | Read one client without secret |
POST /organizations/{org_id}/auth-clients/{id}/rotate | Rotate secret and token epoch |
POST /organizations/{org_id}/auth-clients/{id}/disable | Disable a client |
POST /organizations/{org_id}/auth-clients/{id}/enable | Re-enable a client |
DELETE /organizations/{org_id}/auth-clients/{id} | Delete a disabled client |
Rotating a client secret also advances the client’s token epoch. Existing client-bound refresh chains stop working after rotation.
Common errors
invalid_request: the audience is malformed or multi-valued, the resource is malformed or multi-valued, client credentials are missing, or a required form field is absent.invalid_target: the organization slug does not exist, SSO is not configured, API Clients are not enabled, the resource does not resolve to a project in that organization, the project is inactive or deleted, or the requesting user is not a member of that project.invalid_client: the supplied client credentials are wrong.invalid_grant: the subject token is invalid, expired, replayed, or does not match the configuredazpand audience.invalid_scope: requested scopes are not allowed for the client.