Skip to content
← Documentation

Accounts API

Manage accounts. Each account gets its own realm when you create it.

All routes live under /api/v1/accounts and accept a dashboard session. Send it as Authorization: Bearer <token> or in the realmsso_token cookie. A route that names an account checks two things. The caller must belong to the account, and the caller must hold the role that the route needs. The role is viewer for the reads. The role is member to update or delete an account and to claim or verify a domain.

The list route names no account. It returns only the accounts that the caller belongs to. A caller with no membership on an account id gets 403. This includes an id that does not exist.

Roles rank viewer < member < admin, so an account admin passes every check on this page. See Access control. Accounts and isolation explains the realm that each account has.

One route also accepts an API key: POST /api/v1/accounts, the call that makes the first account. It takes a platform-level key. A platform-level key belongs to no account. A platform operator makes it. A platform operator is RealmSSO staff.

The key must hold the scope platform:accounts:create. The wildcard * does not replace that scope.

Every other route in this module refuses an API key with 401. RealmSSO checks this before it checks any scope or membership. So a platform-level key can create an account, but it cannot read, update or delete that account. For the routes on this page, later access needs a dashboard session with a membership on the account.

Create account

POST/api/v1/accounts
FieldTypeNotes
namestringRequired, 1–255 characters
slugstringRequired, 3–63 characters. It is used as a DNS label, so use lowercase letters, digits and internal hyphens only. No leading or trailing hyphen, no xn-- prefix, and not a reserved label. Must be unique.
domainstringOptional. Recorded as a claim and left unverified. See below.
logostring (URL)Optional
brandColorstringOptional. A hex color: # and exactly six hex digits.

The response is 201 with the account in data. The caller becomes an admin member of the new account in the same database transaction. Without that, the creator would have no access to the new account. With a platform-level key, the person who made the key becomes the admin member, not the key.

After the transaction commits, RealmSSO creates the account's realm. A realm is the separate sign-in space of one account. It has its own users, clients, identity provider links and signing keys. The realm name is kr- followed by the account id. It does not contain the slug.

Response fieldMeaning
keycloakRealmThe realm name. It is null until the realm exists.

If realm creation fails, the response is still 201. The realm field in the table above is then null. RealmSSO creates the realm on the first request that needs it, for example the creation of a connection. A slug that exists returns 409. A domain that another account already holds also returns 409.

The slug is a DNS label

The slug is the first label of a per-customer hostname. It also appears in the OIDC client ID org-<slug>. The rules are stricter than URL-safe. A slug that is not a valid DNS label would make a hostname that cannot resolve. A rejected slug returns 400 and names the problem.

A hostname is a label, not a boundary

The code can give each account its own sign-in hostname. Production has this switched off. Even when it is on, nothing at the edge ties a hostname to the realm of that account. The sign-in service answers any realm on any hostname.

The binding that would make a hostname a boundary is designed, and nothing is built. Building now. Do not treat a hostname as isolation. The realm separates one account from another. Everything below the realm is shared.

List accounts

GET/api/v1/accounts

Returns the accounts that the caller belongs to, not every account on the server. The list is paginated with ?page and ?limit (maximum 100). Each item includes a _count.connections field.

Get account

GET/api/v1/accounts/:id

Returns the account and its connection count. A caller with no membership on the id gets 403.

Update account

PATCH/api/v1/accounts/:id

Accepts a partial body of name, domain, logo and brandColor. Each one accepts null to clear it, except name. It does not change the realm. A new domain value runs the same claim logic as the claim route below. A new domain string never inherits the verified status of the old one.

Delete account

DELETE/api/v1/accounts/:id

Destructive

Deletes the account and its connections. The audit log of the account goes with it. The route returns 204 on success.

RealmSSO then deletes the realm of the account. The realm deletion runs after the database commit. It can fail. RealmSSO records the failure, and the request still returns 204, because the account row is already gone.

List the connections of an account

GET/api/v1/accounts/:id/connections

A summary route. It returns every connection of the account, without pagination. See Connections for the full connection routes: create, update, delete and check.

Claim a domain

POST/api/v1/accounts/:id/domain/claim

Records an email domain for this account and issues a new DNS TXT challenge. The body is { "domain": "acme.com" }. The response is 202, not 200. RealmSSO stores the claim, but ownership is not yet verified.

Publish the returned TXT record at _realmsso-challenge.<domain>. Then call the verify route. Two accounts cannot hold the same domain. The check ignores letter case. A collision returns 409.

Verify a claimed domain

POST/api/v1/accounts/:id/domain/verify

Checks the TXT challenge of the claimed domain against DNS now. On a match, it records the verification. No match is a normal 200 with verified: false, not an error, because DNS changes take time. You can call this route again and again.

Verification makes a domain usable for login routing. GET /api/v1/sso/resolve matches a verified domain only. It treats a claimed but unverified domain like a domain that nobody claimed. It gives one answer for an unknown domain, an unverified domain and a domain with no active connection: 200 with ssoAvailable: false. See Accounts and isolation.

Other routes in this module

RouteRoleWhat it does
POST /api/v1/accounts/:id/connection-error-policyadminSets what a connection in error does about new logins. See Connection status.
POST /api/v1/accounts/:id/agent-accessmemberTurns the agent access of the account on or off. See Agent Identity API.