curl --request POST \
--url https://api.reasonmachines.com/v3/organizations/{orgId}/secrets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "NPM_TOKEN",
"value": "npm_...",
"note": "Used by acme/web",
"scope": "organization"
}
'import requests
url = "https://api.reasonmachines.com/v3/organizations/{orgId}/secrets"
payload = {
"name": "NPM_TOKEN",
"value": "npm_...",
"note": "Used by acme/web",
"scope": "organization"
}
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: 'NPM_TOKEN',
value: 'npm_...',
note: 'Used by acme/web',
scope: 'organization'
})
};
fetch('https://api.reasonmachines.com/v3/organizations/{orgId}/secrets', 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/organizations/{orgId}/secrets",
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' => 'NPM_TOKEN',
'value' => 'npm_...',
'note' => 'Used by acme/web',
'scope' => 'organization'
]),
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/organizations/{orgId}/secrets"
payload := strings.NewReader("{\n \"name\": \"NPM_TOKEN\",\n \"value\": \"npm_...\",\n \"note\": \"Used by acme/web\",\n \"scope\": \"organization\"\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/organizations/{orgId}/secrets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"NPM_TOKEN\",\n \"value\": \"npm_...\",\n \"note\": \"Used by acme/web\",\n \"scope\": \"organization\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/organizations/{orgId}/secrets")
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\": \"NPM_TOKEN\",\n \"value\": \"npm_...\",\n \"note\": \"Used by acme/web\",\n \"scope\": \"organization\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"id": "NPM_TOKEN",
"secret_id": "NPM_TOKEN",
"name": "NPM_TOKEN",
"scope": "user",
"result": "created"
}{
"error": "prompt_required"
}{
"error": "missing_bearer",
"message": "Authorization required"
}{
"error": "missing_scope",
"required_scope": "sessions:read"
}{
"error": "secret_name_case_conflict",
"name": "npm_token",
"existing_name": "NPM_TOKEN",
"message": "NPM_TOKEN already exists and differs from npm_token only by letter case. Use NPM_TOKEN or another name."
}{
"error": "secret_value_too_large",
"max_bytes": 65536
}{
"error": {
"type": "rate_limited",
"message": "This API key exceeded its request-rate limit"
}
}Create or update a secret
Upserts a secret by name. Default user scope is personal; organization scope requires owner/admin authority. Legacy repo scope aliases organization scope: repo/provider fields do not isolate secrets to repositories, and the response scope is the scope the secret was stored in. result says whether the name was created or an existing value was updated. Omitting note keeps the stored note; an empty string clears it. A refused name returns invalid_secret_name with a reason (format, reserved, too_long) and a message. Empty, whitespace-only and NUL-containing values are refused. warning is present for a name Sessions never receive because Reason reserves it for its own access or runtime (GH_TOKEN, GITHUB_TOKEN, GITLAB_TOKEN, GH_ENTERPRISE_TOKEN, GITHUB_ENTERPRISE_TOKEN, BUN_*); the secret is stored but not delivered. Values are encrypted and never returned. Shared changes reach live sessions. Missing encryption returns 503 secret_store_unavailable.
secrets:writecurl --request POST \
--url https://api.reasonmachines.com/v3/organizations/{orgId}/secrets \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"name": "NPM_TOKEN",
"value": "npm_...",
"note": "Used by acme/web",
"scope": "organization"
}
'import requests
url = "https://api.reasonmachines.com/v3/organizations/{orgId}/secrets"
payload = {
"name": "NPM_TOKEN",
"value": "npm_...",
"note": "Used by acme/web",
"scope": "organization"
}
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: 'NPM_TOKEN',
value: 'npm_...',
note: 'Used by acme/web',
scope: 'organization'
})
};
fetch('https://api.reasonmachines.com/v3/organizations/{orgId}/secrets', 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/organizations/{orgId}/secrets",
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' => 'NPM_TOKEN',
'value' => 'npm_...',
'note' => 'Used by acme/web',
'scope' => 'organization'
]),
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/organizations/{orgId}/secrets"
payload := strings.NewReader("{\n \"name\": \"NPM_TOKEN\",\n \"value\": \"npm_...\",\n \"note\": \"Used by acme/web\",\n \"scope\": \"organization\"\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/organizations/{orgId}/secrets")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"NPM_TOKEN\",\n \"value\": \"npm_...\",\n \"note\": \"Used by acme/web\",\n \"scope\": \"organization\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/organizations/{orgId}/secrets")
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\": \"NPM_TOKEN\",\n \"value\": \"npm_...\",\n \"note\": \"Used by acme/web\",\n \"scope\": \"organization\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"id": "NPM_TOKEN",
"secret_id": "NPM_TOKEN",
"name": "NPM_TOKEN",
"scope": "user",
"result": "created"
}{
"error": "prompt_required"
}{
"error": "missing_bearer",
"message": "Authorization required"
}{
"error": "missing_scope",
"required_scope": "sessions:read"
}{
"error": "secret_name_case_conflict",
"name": "npm_token",
"existing_name": "NPM_TOKEN",
"message": "NPM_TOKEN already exists and differs from npm_token only by letter case. Use NPM_TOKEN or another name."
}{
"error": "secret_value_too_large",
"max_bytes": 65536
}{
"error": {
"type": "rate_limited",
"message": "This API key exceeded its request-rate limit"
}
}Authorizations
Your Reason API key from Settings › API keys. New keys use reason_; legacy ara_ keys remain accepted. Keys are capability-scoped: run, mcp:read, mcp:write, secrets:read, secrets:write, sessions:read, sessions:debug, knowledge:read, memory:read, memory:write, skills:read, skills:write, repos:read, repos:write, reviews:read, reviews:write, deployment:read, analytics:read, org:read, org:write, attachments:read, attachments:write, guardrails:read, guardrails:write, network:read, network:write, automations:read, automations:write, agent_auth:read. network:read and network:write are opt-in, excluded from default keys. network:read reads the saved workspace network policy; network:write replaces custom allowed hosts and requires a current organization owner/admin. Legacy project PATCH requests containing network_allowed_hosts also require network:write plus their existing run scope and project management permission; saved project hosts do not affect current session egress. Only owners/admins can mint or manage keys with network:write; keys cannot grant themselves more authority. See /docs/network-policies for replacement semantics and runtime applicability. mcp:write manages MCP server configuration only; it does not authorize remote MCP-tool execution. sessions:read reads sessions, including the assistant, reasoning and tool activity in their events. sessions:debug is privileged: only for organization owners/admins, it expands session events to the full diagnostic projection (diagnostic event kinds, status text and raw event metadata).
Path Parameters
Organization id or slug. Resolve it with GET /v3/self.
Body
Environment variable name, e.g. NPM_TOKEN: letters, digits and underscores, starting with a letter, at most 128 characters. Upper and lower case are both accepted, but a new name may not differ only by case from one already stored in the same scope. Names Reason's runtime or the Session's shell own (PATH, HOME, USER, ...) are refused.
The secret value: not empty, not only whitespace, no NUL byte. Stored encrypted and never returned.
Optional usage guidance for agents and workspace members. Include the repository name when the secret is intended for one codebase. Omit to keep the stored note; send an empty string to clear it. Personal (user) secrets hold no note.
2000organization, user, repo Deprecated context field for the repo compatibility scope. The value is stored workspace-wide.
Deprecated provider context for the repo compatibility scope.
github Response
Secret stored.

