---
title: "Create portal session"
method: POST
path: "/v2/portal.createSession"
tags: ["portal"]
---

# Create portal session

`POST /v2/portal.createSession`

Create a portal session for an end user and get the URL to redirect them to.

The URL carries a single-use exchange code valid for 15 minutes, which the portal
redeems exactly once for a 24-hour access token via `portal.exchangeCode`.

**Required Permissions**

Authorization runs in two stages, and both must pass.

First, your root key must have one of the following permissions:
- `portal.*.create_portal_session` (to mint sessions for any portal in the workspace)
- `portal.<portal_id>.create_portal_session` (to mint sessions for a specific portal)

Second, a session can never carry a capability your root key does not itself
hold. Each requested scope additionally requires the equivalent permission on
every keyspace the portal resolves to:
- `keys:read` requires `api.<api_id>.read_key` **and** `api.<api_id>.read_api`
- `keys:reroll` and `keys:create` require `api.<api_id>.create_key`, plus
  `api.<api_id>.encrypt_key` when the keyspace stores encrypted keys
- `analytics:read` requires `api.<api_id>.read_analytics`

The `*` form of each is also accepted. Requesting a scope you do not hold
returns 403 for the whole request rather than minting a reduced session, so a
missing grant is visible instead of surfacing later as a broken portal.

Missing the portal permission itself returns **404**, not 403: a caller who
cannot mint for a portal is not told whether it exists.

Your root key must also be associated with a workspace that has an enabled portal.

## Request body

- V2PortalCreateSessionRequestBody
  - `portal` string, required — Identifies a resource by either its unique ID or its slug. Accepts a prefixed ID (such as 'proj_' or 'app_') or a slug.
  - `externalId` string, required — The end user's identifier in the customer's system. Accepts arbitrary string values (user IDs, emails, UUIDs, etc.).
  - `scopes` string[], required — The capabilities granted to the end user in the Portal, from a fixed vocabulary. All capabilities are scoped to this end user: key capabilities (`keys:*`) apply only to keys the end user owns within the keyspace configured on the portal, and `analytics:read` returns only the end user's own verification events. An end user can never see another identity's keys or analytics. Tab visibility is derived from the scopes: - Keys tab: any `keys:*` scope - Analytics tab: `analytics:read` - Docs tab: visible when any scope is present `keys:create` is accepted but has no portal route behind it yet. It is still authorized like the others, so a session minted with it required `create_key` on the keyspace at mint time, and a future portal create-key route inherits an enforced ceiling rather than trusting sessions minted while the capability was inert. Each scope requires the equivalent permission on your own root key. See Required Permissions on this operation.
  - `preview` boolean — When true, creates a preview session for testing the portal experience.
  - `returnUrl` string, uri — Absolute URL the end user is sent back to when they leave the portal, or when their session expires mid-visit. Set per session rather than per portal, so one portal can serve several entry points and return each user to the page they came from. When omitted, the portal shows no return link.

## Response `200`

Session created successfully. Redirect the end user to the returned URL.

- V2PortalCreateSessionResponseBody
  - `meta` Meta, required — Metadata object included in every API response. This provides context about the request and is essential for debugging, audit trails, and support inquiries. The `requestId` is particularly important when troubleshooting issues with the Unkey support team.
    - `requestId` string, required — A unique id for this request. Always include this ID when contacting support about a specific API request. This identifier allows Unkey's support team to trace the exact request through logs and diagnostic systems to provide faster assistance.
  - `data` V2PortalCreateSessionResponseData, required
    - `id` string, required — The portal session's identifier. Not a credential: it is safe to log and to store against your own records of the end user's visit.
    - `url` string, uri, required — The full portal URL to redirect the end user to. Carries a single-use exchange code that is valid for 15 minutes.

## Other responses

- `400` — Bad request
- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not Found
- `429` — Too Many Requests
- `500` — Internal server error

---

[API](https://skmtc.net/unkeyed/apis/unkey-api.md) · [All operations](https://skmtc.net/unkeyed/apis/unkey-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/unkeyed/unkey-api/revisions/d3689bca6458/schema)
