Sign a user in
Your backend mints a session
Call
portal.createSession with the portal, the user’s externalId, and the scopes they should have. You get { "id": "ps_...", "url": "https://portal.unkey.com/?code=pec_..." }. The URL works once, within 15 minutes. To put the URL on your own domain, pass a verified custom domain as hostname.The portal exchanges the code
The portal swaps the code for an access token that lasts 24 hours. If the code is expired, already used, or unknown, the user sees
err:unkey:authentication:portal_session_not_found, “Session is invalid, expired, or has already been used.”Request
Create a root key with the permissions listed on this page under Root Keys in the dashboard sidebar. CLI commands can instead use a session from unkey login with the required permissions. See Permission reference for every permission.
The user’s ID in your system, 1 to 256 characters. A session for
user_123 shows the keys whose identity is user_123.One or more of
keys:read, keys:reroll, and analytics:read. Scopes decide what the user can see and do. See What portal users can do. keys:reroll and analytics:read each need keys:read too. Without it, the request fails with 400 and err:unkey:application:invalid_input.Full URL, up to 500 characters, to send the user back to when they leave or the session expires. You set it per session, so different users can go back to different pages.
Permissions your root key needs
Your root key needsunkey:v1:<workspace_id>:projects/<project_id>/portals/<portal_id>/sessions/*#write. Write permission on the portal itself does not grant permission to mint sessions. Without the session permission, the request fails with 404, or with 403 naming the missing permission if your root key can read the portal or its sessions.
It also needs the following grants for each scope on every keyspace the portal shows. Each resource in the table has the prefix unkey:v1:<workspace_id>:projects/<project_id>/keyspaces/<keyspace_id>:
Broader URN grants that cover these resources and actions are also accepted. A grant on one concrete key cannot satisfy the
keys/* requirement. If the root key is missing a scope’s grant, the whole request fails with 403. A disabled portal can’t create sessions at all.
What ends a session
A session ends when its 24 hour token expires, or when the portal is disabled, deleted, or pointed at a different keyspace or app. That usually takes effect within about 10 seconds, but can take up to 5 minutes. Re-enabling a disabled portal doesn’t bring its sessions back. To log one end user out early, callportal.revokeSession with the portal and their externalId. It ends every live session they have on that portal.
Revoking sessions requires the same unkey:v1:<workspace_id>:projects/<project_id>/portals/<portal_id>/sessions/*#write grant as creating a session, or a broader grant that includes it. A root key or dashboard session can call this endpoint with that permission. Permission on the portal itself does not grant permission to revoke sessions. Without read access to the portal’s sessions, the request fails with 404. With read access but without write access, it fails with 403 naming the missing permission.
To see who is signed in, call portal.listSessions with the portal. It returns one entry per end user with a session that has not expired or been revoked, including sessions whose portal URL was not opened yet, and each end user’s sessions and scopes. Results are ordered by externalId, paginated with cursor, and can be filtered with a case-sensitive search prefix. It needs unkey:v1:<workspace_id>:projects/<project_id>/portals/<portal_id>/sessions/*#read or the same resource with #write. Reading the portal itself is not enough. Without the session permission, the request fails with 404.
Creating, exchanging, and revoking sessions write portal.session.create, portal.session.exchange, and portal.session.revoke audit log entries. What the user does in the portal is logged under a portalEndUser actor.