curl --request POST \
--url https://api.photon.codes/v1/projects/{projectId}/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"name": "Example key",
"permissions": [
"project:read"
]
}
'import requests
url = "https://api.photon.codes/v1/projects/{projectId}/api-keys"
payload = {
"name": "Example key",
"permissions": ["project:read"]
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({name: 'Example key', permissions: ['project:read']})
};
fetch('https://api.photon.codes/v1/projects/{projectId}/api-keys', 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.photon.codes/v1/projects/{projectId}/api-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' => 'Example key',
'permissions' => [
'project:read'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$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.photon.codes/v1/projects/{projectId}/api-keys"
payload := strings.NewReader("{\n \"name\": \"Example key\",\n \"permissions\": [\n \"project:read\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("Authorization", "Bearer <token>")
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.photon.codes/v1/projects/{projectId}/api-keys")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Example key\",\n \"permissions\": [\n \"project:read\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.photon.codes/v1/projects/{projectId}/api-keys")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Example key\",\n \"permissions\": [\n \"project:read\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"apiKey": {
"apiKeyId": "<string>",
"createdAt": "2026-01-01T00:00:00.000Z",
"createdByAccountId": "<string>",
"expiresAt": "2026-01-01T00:00:00.000Z",
"name": "<string>",
"permissions": [
"<string>"
],
"permissionsUpdatedAt": "2026-01-01T00:00:00.000Z",
"projectId": "<string>",
"revokedAt": "2026-01-01T00:00:00.000Z"
},
"secret": "<string>"
}{
"code": "IDEMPOTENCY_KEY_REQUIRED",
"status": 400,
"title": "Idempotency Key Required",
"type": "https://photon.codes/docs/problems/idempotency-key-required"
}{
"code": "NOT_AUTHENTICATED",
"status": 401,
"title": "Not Authenticated",
"type": "https://photon.codes/docs/problems/not-authenticated"
}{
"code": "FORBIDDEN",
"status": 403,
"title": "Forbidden",
"type": "https://photon.codes/docs/problems/forbidden"
}{
"code": "PROJECT_NOT_FOUND",
"status": 404,
"title": "Project Not Found",
"type": "https://photon.codes/docs/problems/project-not-found"
}{
"code": "API_KEY_LIMIT_REACHED",
"status": 409,
"title": "API Key Limit Reached",
"type": "https://photon.codes/docs/problems/api-key-limit-reached"
}{
"code": "PROJECT_DELETED",
"status": 410,
"title": "Project Deleted",
"type": "https://photon.codes/docs/problems/project-deleted"
}{
"code": "PAYLOAD_TOO_LARGE",
"status": 413,
"title": "Payload Too Large",
"type": "https://photon.codes/docs/problems/payload-too-large"
}{
"code": "VALIDATION_FAILED",
"issues": [
{
"code": "invalid_type",
"location": "json",
"message": "Expected a string.",
"path": "/name"
}
],
"status": 422,
"title": "Request Validation Failed",
"type": "https://photon.codes/docs/problems/validation-failed"
}{
"code": "INTERNAL_ERROR",
"status": 500,
"title": "Internal Server Error",
"type": "https://photon.codes/docs/problems/internal-error"
}{
"code": "UPSTREAM_FAILURE",
"status": 502,
"title": "Upstream Service Failure",
"type": "https://photon.codes/docs/problems/upstream-failure"
}{
"code": "UPSTREAM_UNAVAILABLE",
"status": 503,
"title": "Upstream Service Unavailable",
"type": "https://photon.codes/docs/problems/upstream-unavailable"
}{
"code": "REQUEST_TIMEOUT",
"status": 504,
"title": "Request Timeout",
"type": "https://photon.codes/docs/problems/request-timeout"
}Create project API key
Creates a key bound to the selected project using the supplied name, permissions and optional expiry. The secret is returned only in this response and in idempotent replays of it; store it securely because other reads never return it. The key is scoped to this project and does not grant account-level access. Supply the required Idempotency-Key header.
curl --request POST \
--url https://api.photon.codes/v1/projects/{projectId}/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"name": "Example key",
"permissions": [
"project:read"
]
}
'import requests
url = "https://api.photon.codes/v1/projects/{projectId}/api-keys"
payload = {
"name": "Example key",
"permissions": ["project:read"]
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({name: 'Example key', permissions: ['project:read']})
};
fetch('https://api.photon.codes/v1/projects/{projectId}/api-keys', 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.photon.codes/v1/projects/{projectId}/api-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' => 'Example key',
'permissions' => [
'project:read'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$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.photon.codes/v1/projects/{projectId}/api-keys"
payload := strings.NewReader("{\n \"name\": \"Example key\",\n \"permissions\": [\n \"project:read\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("Authorization", "Bearer <token>")
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.photon.codes/v1/projects/{projectId}/api-keys")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Example key\",\n \"permissions\": [\n \"project:read\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.photon.codes/v1/projects/{projectId}/api-keys")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"name\": \"Example key\",\n \"permissions\": [\n \"project:read\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"apiKey": {
"apiKeyId": "<string>",
"createdAt": "2026-01-01T00:00:00.000Z",
"createdByAccountId": "<string>",
"expiresAt": "2026-01-01T00:00:00.000Z",
"name": "<string>",
"permissions": [
"<string>"
],
"permissionsUpdatedAt": "2026-01-01T00:00:00.000Z",
"projectId": "<string>",
"revokedAt": "2026-01-01T00:00:00.000Z"
},
"secret": "<string>"
}{
"code": "IDEMPOTENCY_KEY_REQUIRED",
"status": 400,
"title": "Idempotency Key Required",
"type": "https://photon.codes/docs/problems/idempotency-key-required"
}{
"code": "NOT_AUTHENTICATED",
"status": 401,
"title": "Not Authenticated",
"type": "https://photon.codes/docs/problems/not-authenticated"
}{
"code": "FORBIDDEN",
"status": 403,
"title": "Forbidden",
"type": "https://photon.codes/docs/problems/forbidden"
}{
"code": "PROJECT_NOT_FOUND",
"status": 404,
"title": "Project Not Found",
"type": "https://photon.codes/docs/problems/project-not-found"
}{
"code": "API_KEY_LIMIT_REACHED",
"status": 409,
"title": "API Key Limit Reached",
"type": "https://photon.codes/docs/problems/api-key-limit-reached"
}{
"code": "PROJECT_DELETED",
"status": 410,
"title": "Project Deleted",
"type": "https://photon.codes/docs/problems/project-deleted"
}{
"code": "PAYLOAD_TOO_LARGE",
"status": 413,
"title": "Payload Too Large",
"type": "https://photon.codes/docs/problems/payload-too-large"
}{
"code": "VALIDATION_FAILED",
"issues": [
{
"code": "invalid_type",
"location": "json",
"message": "Expected a string.",
"path": "/name"
}
],
"status": 422,
"title": "Request Validation Failed",
"type": "https://photon.codes/docs/problems/validation-failed"
}{
"code": "INTERNAL_ERROR",
"status": 500,
"title": "Internal Server Error",
"type": "https://photon.codes/docs/problems/internal-error"
}{
"code": "UPSTREAM_FAILURE",
"status": 502,
"title": "Upstream Service Failure",
"type": "https://photon.codes/docs/problems/upstream-failure"
}{
"code": "UPSTREAM_UNAVAILABLE",
"status": 503,
"title": "Upstream Service Unavailable",
"type": "https://photon.codes/docs/problems/upstream-unavailable"
}{
"code": "REQUEST_TIMEOUT",
"status": 504,
"title": "Request Timeout",
"type": "https://photon.codes/docs/problems/request-timeout"
}Authorizations
Account Service Key, prefixed with pho_ask_, sent as Authorization: Bearer <key>. Acts on behalf of its owning account, subject to the permissions and credential restrictions of each operation. Account access tokens and OAuth grants also use the Bearer header. In organizations that require SSO, Account Service Keys are not accepted for managing the organization's SSO settings, deleting the organization, or checking whether it can be deleted.
Headers
Identifies one logical mutation across retries.
1 - 255^[\x21-\x7e]{1,255}$Path Parameters
Body
1 - 1281events:read, events:write, platforms:read, platforms:write, project:read, project:write ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$Was this page helpful?