curl --request POST \
--url 'https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=' \
--header 'Content-Type: application/json' \
--data '
{
"projectId": "123e4567-e89b-12d3-a456-426614174000",
"organization": {
"externalId": "acme",
"name": "Acme Inc",
"properties": {
"tenant_id": "acme"
}
},
"user": {
"externalId": "user-42",
"name": "ada@acme.com",
"properties": {
"email": "ada@acme.com",
"region": "EU"
}
},
"expiration": "1h",
"apiKey": "up_embed_************",
"organizationId": "123e4567-e89b-12d3-a456-426614174000"
}
'import requests
url = "https://api.upsolve.ai/v1/api/projects/embed-token?apiKey="
payload = {
"projectId": "123e4567-e89b-12d3-a456-426614174000",
"organization": {
"externalId": "acme",
"name": "Acme Inc",
"properties": { "tenant_id": "acme" }
},
"user": {
"externalId": "user-42",
"name": "ada@acme.com",
"properties": {
"email": "ada@acme.com",
"region": "EU"
}
},
"expiration": "1h",
"apiKey": "up_embed_************",
"organizationId": "123e4567-e89b-12d3-a456-426614174000"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
projectId: '123e4567-e89b-12d3-a456-426614174000',
organization: {externalId: 'acme', name: 'Acme Inc', properties: {tenant_id: 'acme'}},
user: {
externalId: 'user-42',
name: 'ada@acme.com',
properties: {email: 'ada@acme.com', region: 'EU'}
},
expiration: '1h',
apiKey: 'up_embed_************',
organizationId: '123e4567-e89b-12d3-a456-426614174000'
})
};
fetch('https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=', 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.upsolve.ai/v1/api/projects/embed-token?apiKey=",
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([
'projectId' => '123e4567-e89b-12d3-a456-426614174000',
'organization' => [
'externalId' => 'acme',
'name' => 'Acme Inc',
'properties' => [
'tenant_id' => 'acme'
]
],
'user' => [
'externalId' => 'user-42',
'name' => 'ada@acme.com',
'properties' => [
'email' => 'ada@acme.com',
'region' => 'EU'
]
],
'expiration' => '1h',
'apiKey' => 'up_embed_************',
'organizationId' => '123e4567-e89b-12d3-a456-426614174000'
]),
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;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.upsolve.ai/v1/api/projects/embed-token?apiKey="
payload := strings.NewReader("{\n \"projectId\": \"123e4567-e89b-12d3-a456-426614174000\",\n \"organization\": {\n \"externalId\": \"acme\",\n \"name\": \"Acme Inc\",\n \"properties\": {\n \"tenant_id\": \"acme\"\n }\n },\n \"user\": {\n \"externalId\": \"user-42\",\n \"name\": \"ada@acme.com\",\n \"properties\": {\n \"email\": \"ada@acme.com\",\n \"region\": \"EU\"\n }\n },\n \"expiration\": \"1h\",\n \"apiKey\": \"up_embed_************\",\n \"organizationId\": \"123e4567-e89b-12d3-a456-426614174000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=")
.header("Content-Type", "application/json")
.body("{\n \"projectId\": \"123e4567-e89b-12d3-a456-426614174000\",\n \"organization\": {\n \"externalId\": \"acme\",\n \"name\": \"Acme Inc\",\n \"properties\": {\n \"tenant_id\": \"acme\"\n }\n },\n \"user\": {\n \"externalId\": \"user-42\",\n \"name\": \"ada@acme.com\",\n \"properties\": {\n \"email\": \"ada@acme.com\",\n \"region\": \"EU\"\n }\n },\n \"expiration\": \"1h\",\n \"apiKey\": \"up_embed_************\",\n \"organizationId\": \"123e4567-e89b-12d3-a456-426614174000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=")
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 \"projectId\": \"123e4567-e89b-12d3-a456-426614174000\",\n \"organization\": {\n \"externalId\": \"acme\",\n \"name\": \"Acme Inc\",\n \"properties\": {\n \"tenant_id\": \"acme\"\n }\n },\n \"user\": {\n \"externalId\": \"user-42\",\n \"name\": \"ada@acme.com\",\n \"properties\": {\n \"email\": \"ada@acme.com\",\n \"region\": \"EU\"\n }\n },\n \"expiration\": \"1h\",\n \"apiKey\": \"up_embed_************\",\n \"organizationId\": \"123e4567-e89b-12d3-a456-426614174000\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"token": "<string>",
"expiresAt": "<string>",
"projectUserId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"projectOrganizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"userCreated": true,
"organizationCreated": true
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": {}
}
}Get Embed Token
Sign one of your users in to an Upsolve embed. Creates their organization (a project organization, found by organization.externalId) and the user (found by user.externalId within it) on first use, updates the properties you send for row-level security, and returns a project-user token for the iframe. Safe to call on every page load; replaces register-organization + register-user + user-token.
curl --request POST \
--url 'https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=' \
--header 'Content-Type: application/json' \
--data '
{
"projectId": "123e4567-e89b-12d3-a456-426614174000",
"organization": {
"externalId": "acme",
"name": "Acme Inc",
"properties": {
"tenant_id": "acme"
}
},
"user": {
"externalId": "user-42",
"name": "ada@acme.com",
"properties": {
"email": "ada@acme.com",
"region": "EU"
}
},
"expiration": "1h",
"apiKey": "up_embed_************",
"organizationId": "123e4567-e89b-12d3-a456-426614174000"
}
'import requests
url = "https://api.upsolve.ai/v1/api/projects/embed-token?apiKey="
payload = {
"projectId": "123e4567-e89b-12d3-a456-426614174000",
"organization": {
"externalId": "acme",
"name": "Acme Inc",
"properties": { "tenant_id": "acme" }
},
"user": {
"externalId": "user-42",
"name": "ada@acme.com",
"properties": {
"email": "ada@acme.com",
"region": "EU"
}
},
"expiration": "1h",
"apiKey": "up_embed_************",
"organizationId": "123e4567-e89b-12d3-a456-426614174000"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
projectId: '123e4567-e89b-12d3-a456-426614174000',
organization: {externalId: 'acme', name: 'Acme Inc', properties: {tenant_id: 'acme'}},
user: {
externalId: 'user-42',
name: 'ada@acme.com',
properties: {email: 'ada@acme.com', region: 'EU'}
},
expiration: '1h',
apiKey: 'up_embed_************',
organizationId: '123e4567-e89b-12d3-a456-426614174000'
})
};
fetch('https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=', 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.upsolve.ai/v1/api/projects/embed-token?apiKey=",
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([
'projectId' => '123e4567-e89b-12d3-a456-426614174000',
'organization' => [
'externalId' => 'acme',
'name' => 'Acme Inc',
'properties' => [
'tenant_id' => 'acme'
]
],
'user' => [
'externalId' => 'user-42',
'name' => 'ada@acme.com',
'properties' => [
'email' => 'ada@acme.com',
'region' => 'EU'
]
],
'expiration' => '1h',
'apiKey' => 'up_embed_************',
'organizationId' => '123e4567-e89b-12d3-a456-426614174000'
]),
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;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.upsolve.ai/v1/api/projects/embed-token?apiKey="
payload := strings.NewReader("{\n \"projectId\": \"123e4567-e89b-12d3-a456-426614174000\",\n \"organization\": {\n \"externalId\": \"acme\",\n \"name\": \"Acme Inc\",\n \"properties\": {\n \"tenant_id\": \"acme\"\n }\n },\n \"user\": {\n \"externalId\": \"user-42\",\n \"name\": \"ada@acme.com\",\n \"properties\": {\n \"email\": \"ada@acme.com\",\n \"region\": \"EU\"\n }\n },\n \"expiration\": \"1h\",\n \"apiKey\": \"up_embed_************\",\n \"organizationId\": \"123e4567-e89b-12d3-a456-426614174000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=")
.header("Content-Type", "application/json")
.body("{\n \"projectId\": \"123e4567-e89b-12d3-a456-426614174000\",\n \"organization\": {\n \"externalId\": \"acme\",\n \"name\": \"Acme Inc\",\n \"properties\": {\n \"tenant_id\": \"acme\"\n }\n },\n \"user\": {\n \"externalId\": \"user-42\",\n \"name\": \"ada@acme.com\",\n \"properties\": {\n \"email\": \"ada@acme.com\",\n \"region\": \"EU\"\n }\n },\n \"expiration\": \"1h\",\n \"apiKey\": \"up_embed_************\",\n \"organizationId\": \"123e4567-e89b-12d3-a456-426614174000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.upsolve.ai/v1/api/projects/embed-token?apiKey=")
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 \"projectId\": \"123e4567-e89b-12d3-a456-426614174000\",\n \"organization\": {\n \"externalId\": \"acme\",\n \"name\": \"Acme Inc\",\n \"properties\": {\n \"tenant_id\": \"acme\"\n }\n },\n \"user\": {\n \"externalId\": \"user-42\",\n \"name\": \"ada@acme.com\",\n \"properties\": {\n \"email\": \"ada@acme.com\",\n \"region\": \"EU\"\n }\n },\n \"expiration\": \"1h\",\n \"apiKey\": \"up_embed_************\",\n \"organizationId\": \"123e4567-e89b-12d3-a456-426614174000\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"token": "<string>",
"expiresAt": "<string>",
"projectUserId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"projectOrganizationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"userCreated": true,
"organizationCreated": true
}
}{
"status": "error",
"error": {
"message": "<string>",
"code": "<string>",
"details": {}
}
}Authorizations
apiKey MUST be supplied within the request body instead of query
Body
POST /v1/api/projects/embed-token Request body
The Upsolve project the user is signed into. Shown on a workspace's Deploy page.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$Your customer, as a project organization.
Show child attributes
Show child attributes
The person signing in.
Show child attributes
Show child attributes
Upsolve API key, generated under Account → Developer settings in the Upsolve Hub. Keys are user-scoped: pass organizationId alongside the key to select which org to act on.
1"up_embed_************"
Your Upsolve organization's UUID — the org that owns the API key and project. This is NOT a project organization: do not pass a value registered via /projects/register-organization, and do not pass an externalId (only projectOrganizationId accepts those). Omit it when using an org-bound API key — the org is read from the key and any value sent here is ignored. Required only for user-scoped API keys (created under Account → Developer settings) and for the Supabase JWT path, where the org is ambiguous and membership is verified against it.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"123e4567-e89b-12d3-a456-426614174000"
How long the token lives: one of 5m, 15m, 30m, 1h, 2h, 6h, 12h, 24h, 7d. Defaults to 1h.
5m, 15m, 30m, 1h, 2h, 6h, 12h, 24h, 7d Was this page helpful?