Skip to content
← Documentation

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.

PrefixWhat it doesAcceptsReference
/api/v1/authAsk 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/accountsCreate, 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/connectionsCreate, 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/ssoIntegration 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/v2SCIM 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-portalMake 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/observabilitySign-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/auditRead the audit log of one account.Dashboard session or API key.Audit log
/api/v1/webhooksRegister webhooks. Read and redeliver the delivery history. Send a test delivery.Dashboard session or API key.Webhooks API
/api/v1/api-keysCreate, list, change and revoke API keys.Dashboard session only. A key cannot manage keys.Access control
/api/v1/agent-clientsRegister, list, read and revoke agent clients.Dashboard session or API key.Agent Identity API
/api/v1/contactReceives the contact form on this site.No credential.No reference page yet.
/api/v1/productsThe 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/platformTwo 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/me is the exception.
  • GET /api/v1/sso/resolve and GET /api/v1/connections/:id/initiate.
  • The two SCIM discovery routes and the contact routes.
  • GET /health and GET /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.

RoleRights
viewerEvery read on the account.
memberEverything 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.

ScopeWhat it covers
connections:readRead connections and their configuration. Read the integration details of an account.
connections:writeCreate, 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:deleteDelete a connection.
agent-clients:readList and read agent clients.
agent-clients:writeRegister and revoke agent clients.
webhooks:readRead webhooks and their delivery history.
webhooks:writeCreate, change and delete webhooks. Also the test and redeliver routes, because both send a request out.
observability:readThe four account reads under /api/v1/observability.
audit:readThe audit routes.
*Every scope above. It does not include platform:accounts:create.
platform:accounts:createCreate 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/connections
  • GET /api/v1/api-keys
  • GET /api/v1/webhooks
  • GET /api/v1/agent-clients
  • GET /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" } ]
  }
}
CodeStatusMeaning
VALIDATION_ERROR400The body or query failed validation. Only a schema failure carries a details array. A check made in the route code has a message only.
UNAUTHORIZED401The request has no credential, or the dashboard token is invalid or expired. Or the Admin Portal session is invalid.
INVALID_API_KEY401The X-API-Key header did not match an enabled key that has not expired.
MISSING_TOKEN401An Admin Portal route got no portal token.
FORBIDDEN403Authenticated, but not allowed: no membership, a low role, an API key for another account, or a missing scope.
NOT_FOUND404The resource id does not exist.
CONFLICT409A unique value is already taken, for example an account slug.
IDP_STATE_BUSY409Another change to the identity provider settings of this account is in progress. Send the request again.
RATE_LIMITED429The client address sent too many requests.
EXTERNAL_SERVICE_ERROR502A call to the sign-in service failed. Some routes use a more specific 502 code. Their pages name it.
INTERNAL_ERROR500An 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.