curl --request POST \
--url https://api.reasonmachines.com/v3/machines/enrollment-tickets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"install_id": "<string>",
"code_challenge": "<string>",
"roots": [
{
"path_fingerprint": "<string>",
"permissions": {
"read": true,
"write": true,
"execute": true,
"discover_environment": true,
"expose_mcp": false
},
"execution_mode": "confined"
}
],
"ttl_seconds": 61
}
'import requests
url = "https://api.reasonmachines.com/v3/machines/enrollment-tickets"
payload = {
"install_id": "<string>",
"code_challenge": "<string>",
"roots": [
{
"path_fingerprint": "<string>",
"permissions": {
"read": True,
"write": True,
"execute": True,
"discover_environment": True,
"expose_mcp": False
},
"execution_mode": "confined"
}
],
"ttl_seconds": 61
}
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({
install_id: '<string>',
code_challenge: '<string>',
roots: [
{
path_fingerprint: '<string>',
permissions: {
read: true,
write: true,
execute: true,
discover_environment: true,
expose_mcp: false
},
execution_mode: 'confined'
}
],
ttl_seconds: 61
})
};
fetch('https://api.reasonmachines.com/v3/machines/enrollment-tickets', 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.reasonmachines.com/v3/machines/enrollment-tickets",
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([
'install_id' => '<string>',
'code_challenge' => '<string>',
'roots' => [
[
'path_fingerprint' => '<string>',
'permissions' => [
'read' => true,
'write' => true,
'execute' => true,
'discover_environment' => true,
'expose_mcp' => false
],
'execution_mode' => 'confined'
]
],
'ttl_seconds' => 61
]),
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.reasonmachines.com/v3/machines/enrollment-tickets"
payload := strings.NewReader("{\n \"install_id\": \"<string>\",\n \"code_challenge\": \"<string>\",\n \"roots\": [\n {\n \"path_fingerprint\": \"<string>\",\n \"permissions\": {\n \"read\": true,\n \"write\": true,\n \"execute\": true,\n \"discover_environment\": true,\n \"expose_mcp\": false\n },\n \"execution_mode\": \"confined\"\n }\n ],\n \"ttl_seconds\": 61\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.reasonmachines.com/v3/machines/enrollment-tickets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"install_id\": \"<string>\",\n \"code_challenge\": \"<string>\",\n \"roots\": [\n {\n \"path_fingerprint\": \"<string>\",\n \"permissions\": {\n \"read\": true,\n \"write\": true,\n \"execute\": true,\n \"discover_environment\": true,\n \"expose_mcp\": false\n },\n \"execution_mode\": \"confined\"\n }\n ],\n \"ttl_seconds\": 61\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/machines/enrollment-tickets")
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 \"install_id\": \"<string>\",\n \"code_challenge\": \"<string>\",\n \"roots\": [\n {\n \"path_fingerprint\": \"<string>\",\n \"permissions\": {\n \"read\": true,\n \"write\": true,\n \"execute\": true,\n \"discover_environment\": true,\n \"expose_mcp\": false\n },\n \"execution_mode\": \"confined\"\n }\n ],\n \"ttl_seconds\": 61\n}"
response = http.request(request)
puts response.read_body{
"enrollment_ticket": "illustrative-one-use-ticket",
"expires_at": "2026-09-26T09:02:00Z",
"device_kind": "headless",
"lifecycle": "persistent",
"device_expires_at": "2026-10-26T09:00:00Z",
"machine_id": "22222222-2222-4222-8222-222222222222",
"machine_name": "build-box",
"organization_id": "11111111-1111-4111-8111-111111111111"
}Approve a worker with a Machine token
Approves one headless worker installation for the Machine that the token belongs to, in the token’s Workspace. Authenticate only with the Machine token (Authorization: Bearer rmt_...); API keys and OAuth tokens are rejected. The body and one-use ticket are identical to createMachineEnrollmentTicket; the response also names the Workspace and Machine, so a worker needs no other configuration. Tickets are owned by the token creator and stop working if the creator is no longer an owner or admin. Execution feature gates can return 503.
rmt_...) only; API keys and OAuth tokens are rejectedcurl --request POST \
--url https://api.reasonmachines.com/v3/machines/enrollment-tickets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"install_id": "<string>",
"code_challenge": "<string>",
"roots": [
{
"path_fingerprint": "<string>",
"permissions": {
"read": true,
"write": true,
"execute": true,
"discover_environment": true,
"expose_mcp": false
},
"execution_mode": "confined"
}
],
"ttl_seconds": 61
}
'import requests
url = "https://api.reasonmachines.com/v3/machines/enrollment-tickets"
payload = {
"install_id": "<string>",
"code_challenge": "<string>",
"roots": [
{
"path_fingerprint": "<string>",
"permissions": {
"read": True,
"write": True,
"execute": True,
"discover_environment": True,
"expose_mcp": False
},
"execution_mode": "confined"
}
],
"ttl_seconds": 61
}
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({
install_id: '<string>',
code_challenge: '<string>',
roots: [
{
path_fingerprint: '<string>',
permissions: {
read: true,
write: true,
execute: true,
discover_environment: true,
expose_mcp: false
},
execution_mode: 'confined'
}
],
ttl_seconds: 61
})
};
fetch('https://api.reasonmachines.com/v3/machines/enrollment-tickets', 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.reasonmachines.com/v3/machines/enrollment-tickets",
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([
'install_id' => '<string>',
'code_challenge' => '<string>',
'roots' => [
[
'path_fingerprint' => '<string>',
'permissions' => [
'read' => true,
'write' => true,
'execute' => true,
'discover_environment' => true,
'expose_mcp' => false
],
'execution_mode' => 'confined'
]
],
'ttl_seconds' => 61
]),
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.reasonmachines.com/v3/machines/enrollment-tickets"
payload := strings.NewReader("{\n \"install_id\": \"<string>\",\n \"code_challenge\": \"<string>\",\n \"roots\": [\n {\n \"path_fingerprint\": \"<string>\",\n \"permissions\": {\n \"read\": true,\n \"write\": true,\n \"execute\": true,\n \"discover_environment\": true,\n \"expose_mcp\": false\n },\n \"execution_mode\": \"confined\"\n }\n ],\n \"ttl_seconds\": 61\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.reasonmachines.com/v3/machines/enrollment-tickets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"install_id\": \"<string>\",\n \"code_challenge\": \"<string>\",\n \"roots\": [\n {\n \"path_fingerprint\": \"<string>\",\n \"permissions\": {\n \"read\": true,\n \"write\": true,\n \"execute\": true,\n \"discover_environment\": true,\n \"expose_mcp\": false\n },\n \"execution_mode\": \"confined\"\n }\n ],\n \"ttl_seconds\": 61\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/machines/enrollment-tickets")
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 \"install_id\": \"<string>\",\n \"code_challenge\": \"<string>\",\n \"roots\": [\n {\n \"path_fingerprint\": \"<string>\",\n \"permissions\": {\n \"read\": true,\n \"write\": true,\n \"execute\": true,\n \"discover_environment\": true,\n \"expose_mcp\": false\n },\n \"execution_mode\": \"confined\"\n }\n ],\n \"ttl_seconds\": 61\n}"
response = http.request(request)
puts response.read_body{
"enrollment_ticket": "illustrative-one-use-ticket",
"expires_at": "2026-09-26T09:02:00Z",
"device_kind": "headless",
"lifecycle": "persistent",
"device_expires_at": "2026-10-26T09:00:00Z",
"machine_id": "22222222-2222-4222-8222-222222222222",
"machine_name": "build-box",
"organization_id": "11111111-1111-4111-8111-111111111111"
}Authorizations
A Machine token from createMachineToken. It can only create enrollment tickets for its own Machine; every other operation rejects it.
Body
Single-use P-256/PKCE headless enrollment. Persistent TTL defaults to 30 days (maximum 90 days); ephemeral defaults to 1 hour (maximum 24 hours). Minimum TTL is 60 seconds. Root fingerprints and permissions are the approval ceiling; unknown fields, including device_kind and bound_run_id, are rejected.
16 - 160^[A-Za-z0-9_-]{43}$persistent, ephemeral 1 - 32 elementsShow child attributes
Show child attributes
x >= 60Response
One-use ticket bound to the token's Machine.
"headless"persistent, ephemeral ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$Workspace the worker redeems the ticket in.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
