Skip to main content
POST
/
service-accounts
Create Service Account
curl --request POST \
  --url https://api.example.com/service-accounts \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "name": "<string>",
  "scopes": [
    {
      "scope": "<string>",
      "effect": "allow"
    }
  ],
  "expires_in_days": 90,
  "never_expires": false,
  "allow_privileged_scopes": false
}
'
import requests

url = "https://api.example.com/service-accounts"

payload = {
"name": "<string>",
"scopes": [
{
"scope": "<string>",
"effect": "allow"
}
],
"expires_in_days": 90,
"never_expires": False,
"allow_privileged_scopes": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
name: '<string>',
scopes: [{scope: '<string>', effect: 'allow'}],
expires_in_days: 90,
never_expires: false,
allow_privileged_scopes: false
})
};

fetch('https://api.example.com/service-accounts', 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.example.com/service-accounts",
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' => [
[
'scope' => '<string>',
'effect' => 'allow'
]
],
'expires_in_days' => 90,
'never_expires' => false,
'allow_privileged_scopes' => false
]),
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;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://api.example.com/service-accounts"

payload := strings.NewReader("{\n \"name\": \"<string>\",\n \"scopes\": [\n {\n \"scope\": \"<string>\",\n \"effect\": \"allow\"\n }\n ],\n \"expires_in_days\": 90,\n \"never_expires\": false,\n \"allow_privileged_scopes\": false\n}")

req, _ := http.NewRequest("POST", url, payload)

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.example.com/service-accounts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"<string>\",\n \"scopes\": [\n {\n \"scope\": \"<string>\",\n \"effect\": \"allow\"\n }\n ],\n \"expires_in_days\": 90,\n \"never_expires\": false,\n \"allow_privileged_scopes\": false\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.example.com/service-accounts")

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 {\n \"scope\": \"<string>\",\n \"effect\": \"allow\"\n }\n ],\n \"expires_in_days\": 90,\n \"never_expires\": false,\n \"allow_privileged_scopes\": false\n}"

response = http.request(request)
puts response.read_body
{
  "id": "<string>",
  "name": "<string>",
  "principal": "<string>",
  "token_prefix": "<string>",
  "created_at": 123,
  "token": "<string>",
  "user_id": "<string>",
  "scopes": [
    {
      "raw": "<string>",
      "namespace": "<string>",
      "permission": "<string>",
      "id": "<string>",
      "sub_namespace": "<string>",
      "value": "allow"
    }
  ],
  "expires_at": 123,
  "last_used_at": 123,
  "revoked_at": 123,
  "created_by": "<string>"
}
{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Body

application/json
name
string
required

Machine identity name (lowercase slug), e.g. 'claude-code' or 'github-actions'

Maximum string length: 63
scopes
ScopeItem · object[] | null

Scopes granted to the token, as {scope, effect} objects (the shared RBAC write shape; token scopes are grants, so only effect='allow' is accepted). Defaults to run and read scopes: agents:run, teams:run, workflows:run, sessions:read

expires_in_days
integer | null
default:90

Days until the token expires (default: 90)

Required range: 1 <= x <= 3650
never_expires
boolean
default:false

Mint a non-expiring token. Must be set explicitly; overrides expires_in_days.

allow_privileged_scopes
boolean
default:false

Required to grant privileged scopes: any write or delete action, the admin scope, or any service_accounts scope. Privileged tokens must be deliberate, never accidental.

Response

Successful Response

Returned once, at creation. The token is never retrievable again.

id
string
required
name
string
required
principal
string
required

The user_id attached to runs made with this token, e.g. 'sa:claude-code'

token_prefix
string
required

First characters of the token, for display only

created_at
integer
required
token
string
required

The plaintext token. Shown exactly once - store it securely now.

user_id
string | null

The user this account belongs to; None for workspace-level accounts. Distinct from created_by, which records who minted the token.

scopes
ScopeSchema · object[]

Scopes granted to the token, in the shared RBAC read shape

expires_at
integer | null
last_used_at
integer | null
revoked_at
integer | null
created_by
string | null