API overview
What every API route shares: the address, credentials, roles, scopes, error format and rate limit.
Address
Every route in these pages is a path on the API origin. Set the origin once in your shell. Then add the path.
API=https://api.realmsso.com
curl -H "X-API-Key: <your-api-key>" "$API/api/v1/connections?accountId=<account-id>"The API has no interactive explorer in production.
Two checks sit at the root, outside /api. GET /health answers while the process runs. GET /readyz checks the database and the cache. It answers 503 when either fails. The status code of /readyz does not depend on the sign-in service. It is not a status page.
Route prefixes
The server registers the prefixes below. A prefix with no reference page says so in the last column. Some of those prefixes have a concept page: Admin Portal, Invite links and Monitoring and health.
| Prefix | What it does | Accepts | Reference |
|---|---|---|---|
/api/v1/auth | Ask for a sign-in link, sign in, refresh a session, sign out, and read the current user. | No credential. GET /api/v1/auth/me needs a dashboard session. | No reference page yet. |
/api/v1/accounts | Create, read, update and delete accounts. Claim and verify an email domain. Set the connection error policy. Switch agent access. | Dashboard session. A platform-level API key also works on POST /api/v1/accounts. | Accounts API |
/api/v1/connections | Create, read, update, delete and check SAML and OIDC connections. Set the offline policy of a connection. Mark agent principals. | Dashboard session or API key. GET /api/v1/connections/:id/initiate needs no credential. | Connections |
/api/v1/sso | Integration details for your application. The OIDC clients of an account. The email domain lookup. | Dashboard session or API key. GET /api/v1/sso/resolve needs no credential. | No reference page yet. |
/api/scim/v2 | SCIM 2.0 users and groups, pushed by your customer’s directory. | The SCIM bearer token of the account. The two discovery routes need no credential. | SCIM directory sync |
/api/v1/admin-portal | Make an invite link for an IT admin. The calls the Admin Portal makes for that IT admin. | Dashboard session to make the link. The invite-link token, then the portal session, for the rest. | No reference page yet. |
/api/v1/observability | Sign-in and sync events, counts, a timeline and connection health for one account. One read lists any realm that holds more than one account. | Dashboard session or API key. The realm read needs the dashboard session of a platform operator. | No reference page yet. |
/api/v1/audit | Read the audit log of one account. | Dashboard session or API key. | Audit log |
/api/v1/webhooks | Register webhooks. Read and redeliver the delivery history. Send a test delivery. | Dashboard session or API key. | Webhooks API |
/api/v1/api-keys | Create, list, change and revoke API keys. | Dashboard session only. A key cannot manage keys. | Access control |
/api/v1/agent-clients | Register, list, read and revoke agent clients. | Dashboard session or API key. | Agent Identity API |
/api/v1/contact | Receives the contact form on this site. | No credential. | No reference page yet. |
/api/v1/products | The product catalogue, product grants and handouts. | Dashboard session only. The routes need platform-operator authority, except one handout read that a product-team role may also make. | Products and product grants API |
/api/v1/platform | Two platform-wide reads: every account, and the product grant matrix. | Dashboard session of a platform operator. | Products and product grants API |
Authentication
Most routes accept a dashboard session token or an API key. The table above names the credential for each prefix. These routes need no credential, because their caller has none:
- The sign-in routes under
/api/v1/auth.GET /api/v1/auth/meis the exception. GET /api/v1/sso/resolveandGET /api/v1/connections/:id/initiate.- The two SCIM discovery routes and the contact routes.
GET /healthandGET /readyz.
Dashboard session token
A dashboard user signs in with a sign-in link. The sign-in returns a session token and a refresh token. The session token is valid for 24 hours.
Send the session token as Authorization: Bearer <token>. A browser holds it in the realmsso_token cookie.
The refresh token is valid for 30 days. Send it to POST /api/v1/auth/refresh. The response holds a new session token and a new refresh token.
API key
An API key belongs to one account. Send it in the X-API-Key header. Never send it as a Bearer token. A key starts with ss_.
RealmSSO stores the SHA-256 hash of a key and its first 8 characters, never the whole key. The create response shows the key once. A new account key holds the scope * and has no expiry, unless you set otherwise.
A request that carries the X-API-Key header is checked as an API key. A request without it is checked as a dashboard session. With neither credential, the answer is 401 with the code UNAUTHORIZED. A key that is disabled, expired or unknown returns 401 with the code INVALID_API_KEY. Only a dashboard session can make, change or revoke an API key.
Other credentials
SCIM uses a bearer token for each account. See SCIM directory sync. The Admin Portal API takes the IT admin's invite-link token in the X-Admin-Token header. That token works once.
After it, the portal session works through the X-Admin-Session header or the realmsso_admin_session cookie. The whole portal session ends 30 minutes after RealmSSO made the invite link. See Invite links.
Roles on an account
A dashboard session holds one role for each account it belongs to. The roles rank viewer < member < admin. A role also holds every right of the roles below it. RealmSSO reads the role again on each request. It refuses a role that it does not know.
| Role | Rights |
|---|---|
viewer | Every read on the account. |
member | Everything a viewer may do, and every other change. This includes deleting the account and making an invite link for an IT admin. |
admin (account admin) | Everything a member may do, and two more things. An account admin sets the connection error policy and manages API keys: create, re-scope, re-enable and revoke. |
An API key has no role. RealmSSO checks that the key belongs to the account in the request. Then it checks the scopes of the key. No route adds a member, removes a member or changes a role. See Access control.
API key scopes
Scopes apply to API keys only. A dashboard session carries no scopes. The server accepts these scope names and no others.
| Scope | What it covers |
|---|---|
connections:read | Read connections and their configuration. Read the integration details of an account. |
connections:write | Create, change and check connections. Set the offline policy, the assertion grant and agent principals of a connection. Provision the OIDC clients of an account and rotate the client secret. |
connections:delete | Delete a connection. |
agent-clients:read | List and read agent clients. |
agent-clients:write | Register and revoke agent clients. |
webhooks:read | Read webhooks and their delivery history. |
webhooks:write | Create, change and delete webhooks. Also the test and redeliver routes, because both send a request out. |
observability:read | The four account reads under /api/v1/observability. |
audit:read | The audit routes. |
* | Every scope above. It does not include platform:accounts:create. |
platform:accounts:create | Create an account with a platform-level key. A platform-level key belongs to no account. It can hold only this scope and lasts at most 90 days. |
Five scope names that earlier documentation listed no longer exist: accounts:read, accounts:write, scim:read, scim:write and admin-portal:generate. No route ever checked them. Naming one when you make or change a key returns 400.
On account routes, a 403 with the code FORBIDDEN has these causes:
- The dashboard user has no membership on the account.
- The role of the dashboard user is lower than the route needs.
- The API key belongs to another account, or to no account.
- The API key lacks a scope that the route needs.
- The route needs platform-operator authority and the caller lacks it. An API key never holds it.
A few routes use their own 403 code, for example CONNECTION_NOT_BROKERING.
Request and response shape
A single resource comes back as { "data": ... }. A paginated list adds a meta object.
{
"data": [ /* ... */ ],
"meta": { "page": 1, "limit": 20, "total": 42, "totalPages": 3 }
}page and limit are optional query parameters. The server caps limit whatever the caller asks. The cap is 100 for accounts, connections and webhook deliveries. The cap is 200 for the audit log and the observability event feed.
Not every list is paginated
Some lists return a bare { "data": [...] } with no meta. Examples:
GET /api/v1/accounts/:id/connectionsGET /api/v1/api-keysGET /api/v1/webhooksGET /api/v1/agent-clientsGET /api/v1/admin-portal/connections
GET /api/v1/observability/connection-health returns an object with connections and summary. GET /api/v1/observability/timeline returns granularity next to data, not inside meta. Do not write a client that assumes meta is always present.
Errors
Every error on the /api/v1 routes has the same shape. This holds for application errors, validation failures and rate limit rejections. SCIM answers 401, 403 and 404 in its own format. See SCIM directory sync. A SCIM body that fails validation gets the standard format below.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [ { "path": "name", "message": "Required" } ]
}
}| Code | Status | Meaning |
|---|---|---|
| VALIDATION_ERROR | 400 | The body or query failed validation. Only a schema failure carries a details array. A check made in the route code has a message only. |
| UNAUTHORIZED | 401 | The request has no credential, or the dashboard token is invalid or expired. Or the Admin Portal session is invalid. |
| INVALID_API_KEY | 401 | The X-API-Key header did not match an enabled key that has not expired. |
| MISSING_TOKEN | 401 | An Admin Portal route got no portal token. |
| FORBIDDEN | 403 | Authenticated, but not allowed: no membership, a low role, an API key for another account, or a missing scope. |
| NOT_FOUND | 404 | The resource id does not exist. |
| CONFLICT | 409 | A unique value is already taken, for example an account slug. |
| IDP_STATE_BUSY | 409 | Another change to the identity provider settings of this account is in progress. Send the request again. |
| RATE_LIMITED | 429 | The client address sent too many requests. |
| EXTERNAL_SERVICE_ERROR | 502 | A call to the sign-in service failed. Some routes use a more specific 502 code. Their pages name it. |
| INTERNAL_ERROR | 500 | An unexpected server error. |
The server can refuse a request before any route runs. Malformed JSON and an unsupported media type are examples. The answer has a 4xx status. Its code is BAD_REQUEST or a more specific code.
Rate limit and response headers
RealmSSO limits each client address to 200 requests per window. We measured the limit on production on 2026-10-03: the x-ratelimit-limit response header showed 200. The window is 60 seconds in our deployment settings. We did not measure the window. A request over the limit gets 429 with the code RATE_LIMITED.
The API server counts requests in its own memory. The webhook test route and the contact routes have their own, lower limits.
Every API response carries security headers, including HSTS for one year. We captured the headers on production on 2026-10-03. A browser request from another origin works only for an origin that RealmSSO allows.
See Accounts API, Connections, Connection status, SCIM directory sync and Webhooks API for the routes. Accounts and isolation explains the realm that each account has.