Skip to main content
Sign a user into the developer portal from your backend, with no second login. After they sign in to your app, ask Unkey for a session for that user and redirect their browser to the URL you get back. Your root key never reaches the browser.

Sign a user in

1

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.
2

Redirect the user

Send the browser to url.
3

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.”
4

The user works, then leaves

The user only sees their own keys. When the token expires, or they click the return link, the portal sends them to the returnUrl you set. Without one, there’s no return link. To let them back in, create a new session.
You only call portal.exchangeCode yourself if you build your own portal front end. It returns { "accessToken": "pat_...", "expiresAt": <ms> }.

Request

You need a root key with the permissions listed on this page. Create one in the dashboard under Settings > Root Keys. See Permission reference for every permission.
string
required
The portal’s pc_ ID or slug.
string
required
The user’s ID in your system, 1 to 256 characters. A session for user_123 shows the keys whose identity is user_123.
string[]
required
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.
string
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.
boolean
default:"false"
Create a preview session to try the portal yourself.

Permissions your root key needs

Your root key needs portal.*.create_portal_session or portal.<portalId>.create_portal_session. Without it, the request fails with 404, not 403. It also needs a matching permission for each scope, on every keyspace the portal shows, either as * or for each api_ ID: If the root key is missing one, 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 deleted or pointed at a different keyspace or app. That usually takes effect within about 10 seconds, but can take up to 5 minutes. Disabling a portal doesn’t end live sessions. You can’t end a single session, so choose scopes with the 24 hour lifetime in mind. Creating and exchanging a session write portal.session.create and portal.session.exchange audit log entries. What the user does in the portal is logged under a portalEndUser actor.

Errors

Last modified on September 29, 2026