curl --request GET \
--url https://app.testorim.com/api/me \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.testorim.com/api/me"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.testorim.com/api/me', 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://app.testorim.com/api/me",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.testorim.com/api/me"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.testorim.com/api/me")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.testorim.com/api/me")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"user": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"email": "jsmith@example.com",
"name": "<string>",
"imageUrl": "<string>"
},
"organization": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"slug": "<string>",
"kind": "personal",
"plan": "free",
"subscriptionStatus": "none",
"limits": {
"monthlyRunLimit": 123,
"monthlyBrowserMinutesLimit": 123,
"monthlyLlmBudgetCents": 123
},
"role": "owner"
},
"currentOrg": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"slug": "<string>",
"kind": "personal",
"plan": "free",
"subscriptionStatus": "none",
"limits": {
"monthlyRunLimit": 123,
"monthlyBrowserMinutesLimit": 123,
"monthlyLlmBudgetCents": 123
},
"role": "owner"
},
"personalOrg": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"slug": "<string>",
"kind": "personal",
"plan": "free",
"subscriptionStatus": "none",
"limits": {
"monthlyRunLimit": 123,
"monthlyBrowserMinutesLimit": 123,
"monthlyLlmBudgetCents": 123
},
"role": "owner"
}
}{
"error": "Invalid or revoked API key"
}{
"error": "<string>",
"retryAfter": 123
}Identify the presented credential
Returns the user the credential belongs to plus the active and personal organizations. The cheapest way to validate a key. This is exactly what testorim login calls before it writes the key to ~/.testorim/config.json. For an API key, currentOrg is always the organization the key is bound to. organization is a backwards-compatible alias of currentOrg with identical content.
curl --request GET \
--url https://app.testorim.com/api/me \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.testorim.com/api/me"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.testorim.com/api/me', 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://app.testorim.com/api/me",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.testorim.com/api/me"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://app.testorim.com/api/me")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.testorim.com/api/me")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"user": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"email": "jsmith@example.com",
"name": "<string>",
"imageUrl": "<string>"
},
"organization": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"slug": "<string>",
"kind": "personal",
"plan": "free",
"subscriptionStatus": "none",
"limits": {
"monthlyRunLimit": 123,
"monthlyBrowserMinutesLimit": 123,
"monthlyLlmBudgetCents": 123
},
"role": "owner"
},
"currentOrg": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"slug": "<string>",
"kind": "personal",
"plan": "free",
"subscriptionStatus": "none",
"limits": {
"monthlyRunLimit": 123,
"monthlyBrowserMinutesLimit": 123,
"monthlyLlmBudgetCents": 123
},
"role": "owner"
},
"personalOrg": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"slug": "<string>",
"kind": "personal",
"plan": "free",
"subscriptionStatus": "none",
"limits": {
"monthlyRunLimit": 123,
"monthlyBrowserMinutesLimit": 123,
"monthlyLlmBudgetCents": 123
},
"role": "owner"
}
}{
"error": "Invalid or revoked API key"
}{
"error": "<string>",
"retryAfter": 123
}Authorizations
Send Authorization: Bearer tst_live_….
Format (services/api-keys.ts): the literal prefix tst_live_
followed by 24 random bytes rendered as 32 base64url characters.
Only the SHA-256 hash is stored server-side. The shape check that
routes a token down the API-key path rather than the Clerk JWT path
requires the tst_live_ prefix and a total length of at least 25
characters.
Keys are minted in the dashboard at /settings/team. The same header
also accepts a Clerk session JWT, which is how the web app
authenticates, but the JWT path is out of scope for this document.
Response
OK
Show child attributes
Show child attributes
Backwards-compatible alias of currentOrg with identical content. Prefer currentOrg in new integrations.
Show child attributes
Show child attributes
For an API key, always the organization the key is bound to, every request in this document is scoped to it.
Show child attributes
Show child attributes
The caller's auto-provisioned personal workspace. Not necessarily the org an API key acts on.
Show child attributes
Show child attributes

