curl -sS -X POST "https://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys" \
-H "Authorization: Bearer $PHOSRA_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
}'const BASE = "https://phosra-api-sandbox-production.up.railway.app/api/v1";
const res = await fetch(`${BASE}/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.PHOSRA_SESSION_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
}),
});
console.log(res.status, await res.json());
import os, requests
BASE = "https://phosra-api-sandbox-production.up.railway.app/api/v1"
res = requests.post(
f"{BASE}/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys",
headers={"Authorization": f"Bearer {os.environ['PHOSRA_SESSION_TOKEN']}"},
json={
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
},
)
print(res.status_code, res.json())
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
base := "https://phosra-api-sandbox-production.up.railway.app/api/v1"
body := bytes.NewBufferString(`{
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
}`)
req, _ := http.NewRequest("POST", base+"/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PHOSRA_SESSION_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/{orgId}/keys",
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([
'name' => '<string>',
'scopes' => [
'read:children',
'read:policies'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/{orgId}/keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"scopes\": [\n \"read:children\",\n \"read:policies\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/{orgId}/keys")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"scopes\": [\n \"read:children\",\n \"read:policies\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"id": "6df6a2a1-ed1b-42b6-8901-3c16aca1ff85",
"org_id": "ef35953a-44f5-473a-a86f-1341163fa40e",
"name": "docs-dx24-sample",
"key_prefix": "phosra_test_d0a474be",
"environment": "test",
"scopes": [
"read:children",
"read:policies"
],
"created_by": "06c5090f-ea5b-4841-a611-e8c9d67df0c5",
"created_at": "2026-07-06T09:23:04.301651883Z",
"key": "phosra_test_d0a474be988b6d558b350d3552e9d8fdd8e7ace606110155ada71849be7f71ff"
}{
"error": "Bad Request",
"message": "invalid scope requested: bogus:scope",
"code": 400
}{
"error": "Unauthorized",
"message": "missing authorization header",
"code": 401
}{
"error": "Forbidden",
"message": "insufficient role for this action",
"code": 403
}{
"error": "Not Found",
"message": "developer org not found",
"code": 404
}{
"error": "Conflict",
"message": "organization slug already exists, please try again",
"code": 409
}{
"error": "Too Many Requests",
"message": "rate limit exceeded",
"code": 429
}{
"error": "Internal Server Error",
"message": "internal error",
"code": 500
}Create API key
Creates an API key for the organization and returns the full secret exactly once. Store it securely — it cannot be retrieved again.
curl -sS -X POST "https://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys" \
-H "Authorization: Bearer $PHOSRA_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
}'const BASE = "https://phosra-api-sandbox-production.up.railway.app/api/v1";
const res = await fetch(`${BASE}/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.PHOSRA_SESSION_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
}),
});
console.log(res.status, await res.json());
import os, requests
BASE = "https://phosra-api-sandbox-production.up.railway.app/api/v1"
res = requests.post(
f"{BASE}/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys",
headers={"Authorization": f"Bearer {os.environ['PHOSRA_SESSION_TOKEN']}"},
json={
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
},
)
print(res.status_code, res.json())
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
)
func main() {
base := "https://phosra-api-sandbox-production.up.railway.app/api/v1"
body := bytes.NewBufferString(`{
"name": "Server (sandbox)",
"environment": "test",
"scopes": [
"policies:write",
"enforcement:write"
]
}`)
req, _ := http.NewRequest("POST", base+"/developers/orgs/org_01HXV2K8Z3QJ5N6P7R8S9T0U1V/keys", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PHOSRA_SESSION_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(resp.Status, string(out))
}
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/{orgId}/keys",
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([
'name' => '<string>',
'scopes' => [
'read:children',
'read:policies'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/{orgId}/keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"scopes\": [\n \"read:children\",\n \"read:policies\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://phosra-api-sandbox-production.up.railway.app/api/v1/developers/orgs/{orgId}/keys")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"<string>\",\n \"scopes\": [\n \"read:children\",\n \"read:policies\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"id": "6df6a2a1-ed1b-42b6-8901-3c16aca1ff85",
"org_id": "ef35953a-44f5-473a-a86f-1341163fa40e",
"name": "docs-dx24-sample",
"key_prefix": "phosra_test_d0a474be",
"environment": "test",
"scopes": [
"read:children",
"read:policies"
],
"created_by": "06c5090f-ea5b-4841-a611-e8c9d67df0c5",
"created_at": "2026-07-06T09:23:04.301651883Z",
"key": "phosra_test_d0a474be988b6d558b350d3552e9d8fdd8e7ace606110155ada71849be7f71ff"
}{
"error": "Bad Request",
"message": "invalid scope requested: bogus:scope",
"code": 400
}{
"error": "Unauthorized",
"message": "missing authorization header",
"code": 401
}{
"error": "Forbidden",
"message": "insufficient role for this action",
"code": 403
}{
"error": "Not Found",
"message": "developer org not found",
"code": 404
}{
"error": "Conflict",
"message": "organization slug already exists, please try again",
"code": 409
}{
"error": "Too Many Requests",
"message": "rate limit exceeded",
"code": 429
}{
"error": "Internal Server Error",
"message": "internal error",
"code": 500
}phosra_ key. You never need a phosra_ key to mint your first phosra_ key. The full zero-to-key path — sign up, auto-provision an org, mint the first key (console or one curl) — is Create your account & get keys. The orgId above is the org that page returns.Authorizations
A logged-in user session bearer token (WorkOS AuthKit access token from signup/login).
Path Parameters
UUID of the developer organization.
Body
Human-readable label for the key.
Key environment — test keys cannot call production endpoints.
test, live Optional permission scopes granted to this key. Omit the field entirely (or send []) to mint an unscoped key — the documented zero-to-key body { "name": …, "environment": "test" } does exactly that. Every entry must be one of the values in the enum below; an unknown scope is rejected 400 invalid scope requested.
read:families, write:families, read:children, write:children, read:policies, write:policies, read:enforcement, write:enforcement, read:devices, write:devices, read:webhooks, write:webhooks, read:ratings, read:platforms ["read:children", "read:policies"]
Response
Key created. The raw secret (key) is returned only in this response — store it now, it can never be retrieved again. A freshly minted key has no last_used_at, last_used_ip, or expires_at yet (those fields are omitted until set), and key_prefix is the first token of the secret (phosra_test_<8 hex>), safe to display in a dashboard. (Body captured verbatim from the live sandbox.)
An API key. The secret is returned only once at create/regenerate (see DeveloperApiKeyWithSecret).
Unique identifier for this resource.
UUID identifier.
Human-readable display name.
Key prefix.
One of: test, live.
test, live Permission scopes granted to this key; empty means unscoped (full access within the account).
RFC 3339 timestamp.
IP address of the most recent request made with this key; null if never used.
RFC 3339 timestamp of when this value expires.
RFC 3339 timestamp of when the key was revoked; null while the key is active.
UUID identifier.
RFC 3339 timestamp of when the resource was created.
The raw secret key — shown only once.