Exchange a short-lived session token for a long-lived browser session.
This endpoint is unauthenticated. The session token itself serves as proof of authorization. Each token can only be exchanged once; subsequent attempts return 401.
The returned browser session token is valid for 24 hours and should be stored as an httpOnly cookie or used in the Authorization header for subsequent API calls.
from unkey.py import Unkey
with Unkey() as unkey:
res = unkey.portal.exchange_session(session_id="pst_abc123def456")
# Handle response
print(res)import { Unkey } from "@unkey/api";
const unkey = new Unkey();
async function run() {
const result = await unkey.portal.exchangeSession({
sessionId: "pst_abc123def456",
});
console.log(result);
}
run();package main
import(
"context"
unkey "github.com/unkeyed/sdks/api/go/v2"
"github.com/unkeyed/sdks/api/go/v2/models/components"
"log"
)
func main() {
ctx := context.Background()
s := unkey.New()
res, err := s.Portal.ExchangeSession(ctx, components.V2PortalExchangeSessionRequestBody{
SessionID: "pst_abc123def456",
})
if err != nil {
log.Fatal(err)
}
if res.V2PortalExchangeSessionResponseBody != nil {
// handle response
}
}curl --request POST \
--url https://api.unkey.com/v2/portal.exchangeSession \
--header 'Content-Type: application/json' \
--data '
{
"sessionId": "pst_abc123def456"
}
'const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({sessionId: 'pst_abc123def456'})
};
fetch('https://api.unkey.com/v2/portal.exchangeSession', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.unkey.com/v2/portal.exchangeSession",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'sessionId' => 'pst_abc123def456'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://api.unkey.com/v2/portal.exchangeSession")
.header("Content-Type", "application/json")
.body("{\n \"sessionId\": \"pst_abc123def456\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unkey.com/v2/portal.exchangeSession")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"sessionId\": \"pst_abc123def456\"\n}"
response = http.request(request)
puts response.read_body{
"meta": {
"requestId": "req_123"
},
"data": {
"token": "ps_xyz789abc123",
"expiresAt": 1711386400000
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found",
"errors": [
{
"location": "body.permissions[0].name",
"message": "Must be at least 3 characters long",
"fix": "Ensure the name uses only alphanumeric characters, underscores, and hyphens"
}
]
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}Body
The session token ID received from portal.createSession.
Must be valid, unexpired, and not previously exchanged.
1"pst_abc123def456"
Response
Session exchanged successfully. Use the returned token for subsequent API calls.
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.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Was this page helpful?
from unkey.py import Unkey
with Unkey() as unkey:
res = unkey.portal.exchange_session(session_id="pst_abc123def456")
# Handle response
print(res)import { Unkey } from "@unkey/api";
const unkey = new Unkey();
async function run() {
const result = await unkey.portal.exchangeSession({
sessionId: "pst_abc123def456",
});
console.log(result);
}
run();package main
import(
"context"
unkey "github.com/unkeyed/sdks/api/go/v2"
"github.com/unkeyed/sdks/api/go/v2/models/components"
"log"
)
func main() {
ctx := context.Background()
s := unkey.New()
res, err := s.Portal.ExchangeSession(ctx, components.V2PortalExchangeSessionRequestBody{
SessionID: "pst_abc123def456",
})
if err != nil {
log.Fatal(err)
}
if res.V2PortalExchangeSessionResponseBody != nil {
// handle response
}
}curl --request POST \
--url https://api.unkey.com/v2/portal.exchangeSession \
--header 'Content-Type: application/json' \
--data '
{
"sessionId": "pst_abc123def456"
}
'const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({sessionId: 'pst_abc123def456'})
};
fetch('https://api.unkey.com/v2/portal.exchangeSession', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.unkey.com/v2/portal.exchangeSession",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'sessionId' => 'pst_abc123def456'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://api.unkey.com/v2/portal.exchangeSession")
.header("Content-Type", "application/json")
.body("{\n \"sessionId\": \"pst_abc123def456\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.unkey.com/v2/portal.exchangeSession")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"sessionId\": \"pst_abc123def456\"\n}"
response = http.request(request)
puts response.read_body{
"meta": {
"requestId": "req_123"
},
"data": {
"token": "ps_xyz789abc123",
"expiresAt": 1711386400000
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found",
"errors": [
{
"location": "body.permissions[0].name",
"message": "Must be at least 3 characters long",
"fix": "Ensure the name uses only alphanumeric characters, underscores, and hyphens"
}
]
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}{
"meta": {
"requestId": "req_123"
},
"error": {
"detail": "Property foo is required but is missing.",
"status": 404,
"title": "Not Found",
"type": "https://unkey.com/docs/errors/unkey/resource/not_found"
}
}