curl --request POST \
--url https://api.erdo.ai/v1/tokens \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"access": "<string>",
"capabilities": [
"<string>"
],
"expires_in_days": 123,
"name": "<string>",
"organization_id": "<string>",
"resource_slugs": [
"<string>"
]
}
'import requests
url = "https://api.erdo.ai/v1/tokens"
payload = {
"access": "<string>",
"capabilities": ["<string>"],
"expires_in_days": 123,
"name": "<string>",
"organization_id": "<string>",
"resource_slugs": ["<string>"]
}
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({
access: '<string>',
capabilities: ['<string>'],
expires_in_days: 123,
name: '<string>',
organization_id: '<string>',
resource_slugs: ['<string>']
})
};
fetch('https://api.erdo.ai/v1/tokens', 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.erdo.ai/v1/tokens",
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([
'access' => '<string>',
'capabilities' => [
'<string>'
],
'expires_in_days' => 123,
'name' => '<string>',
'organization_id' => '<string>',
'resource_slugs' => [
'<string>'
]
]),
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.erdo.ai/v1/tokens"
payload := strings.NewReader("{\n \"access\": \"<string>\",\n \"capabilities\": [\n \"<string>\"\n ],\n \"expires_in_days\": 123,\n \"name\": \"<string>\",\n \"organization_id\": \"<string>\",\n \"resource_slugs\": [\n \"<string>\"\n ]\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.erdo.ai/v1/tokens")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"access\": \"<string>\",\n \"capabilities\": [\n \"<string>\"\n ],\n \"expires_in_days\": 123,\n \"name\": \"<string>\",\n \"organization_id\": \"<string>\",\n \"resource_slugs\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/tokens")
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 \"access\": \"<string>\",\n \"capabilities\": [\n \"<string>\"\n ],\n \"expires_in_days\": 123,\n \"name\": \"<string>\",\n \"organization_id\": \"<string>\",\n \"resource_slugs\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"access": "<string>",
"capabilities": [
"<string>"
],
"default_org": {
"id": "<string>",
"name": "<string>"
},
"expires_at": "<string>",
"hint": "<string>",
"id": "<string>",
"resource_slugs": [
"<string>"
],
"token": "<string>"
}{
"code": "not_found",
"details": {},
"message": "<string>"
}CreateTokenAPI mints a new API token for the caller. The raw token is returned
exactly once.
curl --request POST \
--url https://api.erdo.ai/v1/tokens \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"access": "<string>",
"capabilities": [
"<string>"
],
"expires_in_days": 123,
"name": "<string>",
"organization_id": "<string>",
"resource_slugs": [
"<string>"
]
}
'import requests
url = "https://api.erdo.ai/v1/tokens"
payload = {
"access": "<string>",
"capabilities": ["<string>"],
"expires_in_days": 123,
"name": "<string>",
"organization_id": "<string>",
"resource_slugs": ["<string>"]
}
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({
access: '<string>',
capabilities: ['<string>'],
expires_in_days: 123,
name: '<string>',
organization_id: '<string>',
resource_slugs: ['<string>']
})
};
fetch('https://api.erdo.ai/v1/tokens', 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.erdo.ai/v1/tokens",
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([
'access' => '<string>',
'capabilities' => [
'<string>'
],
'expires_in_days' => 123,
'name' => '<string>',
'organization_id' => '<string>',
'resource_slugs' => [
'<string>'
]
]),
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.erdo.ai/v1/tokens"
payload := strings.NewReader("{\n \"access\": \"<string>\",\n \"capabilities\": [\n \"<string>\"\n ],\n \"expires_in_days\": 123,\n \"name\": \"<string>\",\n \"organization_id\": \"<string>\",\n \"resource_slugs\": [\n \"<string>\"\n ]\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.erdo.ai/v1/tokens")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"access\": \"<string>\",\n \"capabilities\": [\n \"<string>\"\n ],\n \"expires_in_days\": 123,\n \"name\": \"<string>\",\n \"organization_id\": \"<string>\",\n \"resource_slugs\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.erdo.ai/v1/tokens")
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 \"access\": \"<string>\",\n \"capabilities\": [\n \"<string>\"\n ],\n \"expires_in_days\": 123,\n \"name\": \"<string>\",\n \"organization_id\": \"<string>\",\n \"resource_slugs\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"access": "<string>",
"capabilities": [
"<string>"
],
"default_org": {
"id": "<string>",
"name": "<string>"
},
"expires_at": "<string>",
"hint": "<string>",
"id": "<string>",
"resource_slugs": [
"<string>"
],
"token": "<string>"
}{
"code": "not_found",
"details": {},
"message": "<string>"
}Authorizations
An Erdo API key (erdo_api_...) or scoped token (erdo_token_...).
Body
endpoint in any org you belong to, and nothing that writes. It cannot be combined with capabilities.
one of these capability strings, and every unmapped endpoint is denied (fail closed). The vocabulary is closed — see the API docs; unknown strings are rejected. Empty mints a normal unscoped key. Scoped keys can never mint tokens, touch managed-organization surfaces, or connect/delete integrations, regardless of the capabilities granted here.
0 defaults to 30 days — the REST surface intentionally does not expose no-expiry tokens to prevent callers accidentally minting eternal credentials with a minimal payload like {"name": "ci"}.
to the caller's currently-active org. Must be an org the caller belongs to. For scoped keys (capabilities non-empty) this org is a HARD pin: the key can never select another org via X-Organization-ID. identity-ok: token minting; resolveOrgIDOrSlug then user.CreateAPIToken refuses an org the caller is not a member of (orgaccess.UserBelongsToOrg)
workstream calls to these slugs (requires capabilities). Empty = no slug confinement beyond capabilities.

