Invite links
How an invite link becomes an Admin Portal session, what the link allows, and how it differs from the sign-in link of a dashboard user.
An invite link gives your customer's IT admin access to the Admin Portal for one account. The link works once and lasts 30 minutes. The link is a bearer credential. Whoever holds it within those 30 minutes acts as the IT admin. RealmSSO does not check who opens it.
At a glance
| Property | Value |
|---|---|
| Token | 48 random bytes, hex-encoded. |
| What RealmSSO stores | Only a SHA-256 hash of the token. RealmSSO never stores the token itself. |
| Lifetime | 30 minutes from when the link was made. |
| Use | Single use. The first request that presents the token consumes it. |
| Session | One account. It ends 30 minutes after the link was made, even if the IT admin keeps working. |
| Who can make one | A dashboard user with at least the member role on the account. |
| Who receives the link | The IT admin, by email. The dashboard user who made it also receives the link, token included, in the response. |
Making the link
/api/v1/admin-portal/generate-link| Field | Type | Notes |
|---|---|---|
| accountId | string | Required. The account the IT admin will set up. |
| string | Required. A valid email address for the IT admin. | |
| displayName | string | Optional. |
The call needs a dashboard session. RealmSSO then does these steps:
- It generates 48 random bytes and hex-encodes them into the raw token.
- It stores a SHA-256 hash of the token on a new session record. The record also holds the account ID, the IT admin's email, the display name and the expiry time. The expiry is 30 minutes from now.
- It builds the link and tries to email it to the IT admin.
{
"data": {
"sessionId": "clx5a9d3f0007mnop",
"portalUrl": "<portal host>/portal?token=<96 hex characters>&org=<account slug>",
"expiresAt": "2026-10-07T14:30:00.000Z",
"email": "it-admin@customer.test",
"emailSent": true
}
}The call returns 201. portalUrl is the working link. It holds the raw token, so handle the response as a credential. RealmSSO never stores the raw token. It cannot show the link again, so make a new link if you lose it.
emailSent says whether the email went out. If the send fails, the call still returns 201, emailSent is false, and the link works. Give portalUrl to the IT admin another way. The audit record admin_portal.link_generated also records emailSent, with severity warn when the send fails. It never holds the link.
Using the link
The portal reads ?token= from the address and calls the route below. The token travels in the X-Admin-Token header. The server reads the token from that header only. It does not accept the token as a query parameter.
/api/v1/admin-portal/sessionAuthentication runs in two phases.
- Bootstrap. The server hashes the token and consumes it in one conditional write. The write sets
usedAtonly where the hash matches,usedAtis still empty, and the expiry is in the future. Exactly one request wins. Every later presentation of the token matches no record. The first request that carries the token consumes it, on any route. - Continuation. The bootstrap token is now spent. The same response returns a continuation token, a signed token of kind
admin-portal-session. It arrives three ways: anhttpOnlycookie namedrealmsso_admin_session, anx-admin-sessionresponse header, and, onGET /session, the fielddata.continuation. Later requests send it back as theX-Admin-Sessionheader or as that cookie.
The continuation token keeps the session alive. It never extends the session past 30 minutes from when the link was made. The server checks the link's expiry on every request. A late opener needs a new link.
A successful GET /session returns the account's name, slug, logo and accent colour. It also returns the IT admin's email and display name, and the time the session ends. A spent token that comes with a valid continuation token passes. A normal portal client sends the spent token on every call. A spent or expired token with no valid continuation gets 401.
The server keeps the link single use
The consume step runs inside authentication, on the first request that carries the token. Nothing that the portal screen does or omits changes that.
POST /api/v1/admin-portal/session/use still exists. It does nothing except write an admin_portal.session_consumed audit record. Single use does not depend on it.
What the link allows
The session covers one account. Inside it, the IT admin can list the account's connections and change the SCIM settings. The IT admin can also create or replace SAML and OIDC connections. The IT admin has no dashboard access and no other account. The portal screen does less than these routes allow. For example, the portal's OIDC tab does not call the OIDC routes below, so it does not save. See Admin Portal.
# The invite link
<portal host>/portal?token=<96 hex characters>&org=<account slug>
# First request: bootstrap. It consumes the token and returns the continuation
# token in an x-admin-session response header and a realmsso_admin_session cookie.
GET /api/v1/admin-portal/session X-Admin-Token: <raw token>
# Every later request: either header works, or the cookie
GET /api/v1/admin-portal/connections X-Admin-Session: <continuation token>
POST /api/v1/admin-portal/connections/saml X-Admin-Session: <continuation token>
POST /api/v1/admin-portal/connections/oidc X-Admin-Session: <continuation token>
PUT /api/v1/admin-portal/connections/:id/saml X-Admin-Session: <continuation token>
PUT /api/v1/admin-portal/connections/:id/oidc X-Admin-Session: <continuation token>
PUT /api/v1/admin-portal/connections/:id/scim X-Admin-Session: <continuation token>The link is a bearer credential
RealmSSO does not check who holds the link. It checks only that the token is valid, unused and inside its 30 minutes. Send the link to the IT admin by a channel that you trust. Anyone who opens it first acts as the IT admin for that account.
What RealmSSO records
- Making a link. The audit record
admin_portal.link_generatedholds the email, the display name andemailSent. - A portal sign-in. A sign-in event, not an audit record. The first use records a
login.successevent. A spent or expired token sent with no continuation token records alogin.failureevent. Its reason isalready_usedorexpired. See Monitoring and health. - Limits. Audit writes are best effort. See the Audit log page.
The sign-in link is a different thing
A dashboard user signs in with a sign-in link. It is not an invite link. It uses the same token pattern, with a shorter life.
| Property | Invite link | Sign-in link |
|---|---|---|
| For | An IT admin at your customer | A dashboard user |
| Opens | The Admin Portal, for one account | The dashboard |
| Token | 48 random bytes, stored as a SHA-256 hash | 48 random bytes, stored as a SHA-256 hash |
| Lifetime | 30 minutes | 15 minutes |
| Use | Single use | Single use. Only the newest link works. |
| Routes | POST /api/v1/admin-portal/generate-link | POST /api/v1/auth/magic-link and GET /api/v1/auth/verify |