curl --request POST \
--url https://api.reasonmachines.com/v3/organizations/{orgId}/skills \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"name": "Release checklist",
"description": "Use when preparing a production release, deploying changes, or applying the team's release checklist.",
"instructions": "Run the full test suite, update the changelog, then wait for a human approval before deploying.",
"activation_keywords": [
"release",
"deploy"
]
}
EOFimport requests
url = "https://api.reasonmachines.com/v3/organizations/{orgId}/skills"
payload = {
"name": "Release checklist",
"description": "Use when preparing a production release, deploying changes, or applying the team's release checklist.",
"instructions": "Run the full test suite, update the changelog, then wait for a human approval before deploying.",
"activation_keywords": ["release", "deploy"]
}
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: 'Release checklist',
description: 'Use when preparing a production release, deploying changes, or applying the team\'s release checklist.',
instructions: 'Run the full test suite, update the changelog, then wait for a human approval before deploying.',
activation_keywords: ['release', 'deploy']
})
};
fetch('https://api.reasonmachines.com/v3/organizations/{orgId}/skills', 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}/skills",
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' => 'Release checklist',
'description' => 'Use when preparing a production release, deploying changes, or applying the team\'s release checklist.',
'instructions' => 'Run the full test suite, update the changelog, then wait for a human approval before deploying.',
'activation_keywords' => [
'release',
'deploy'
]
]),
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}/skills"
payload := strings.NewReader("{\n \"name\": \"Release checklist\",\n \"description\": \"Use when preparing a production release, deploying changes, or applying the team's release checklist.\",\n \"instructions\": \"Run the full test suite, update the changelog, then wait for a human approval before deploying.\",\n \"activation_keywords\": [\n \"release\",\n \"deploy\"\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.reasonmachines.com/v3/organizations/{orgId}/skills")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Release checklist\",\n \"description\": \"Use when preparing a production release, deploying changes, or applying the team's release checklist.\",\n \"instructions\": \"Run the full test suite, update the changelog, then wait for a human approval before deploying.\",\n \"activation_keywords\": [\n \"release\",\n \"deploy\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/organizations/{orgId}/skills")
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\": \"Release checklist\",\n \"description\": \"Use when preparing a production release, deploying changes, or applying the team's release checklist.\",\n \"instructions\": \"Run the full test suite, update the changelog, then wait for a human approval before deploying.\",\n \"activation_keywords\": [\n \"release\",\n \"deploy\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"skill_id": "skill_example",
"name": "Release checklist",
"description": "Use when preparing a release.",
"instructions": "Run the repository's release checks before deploying.",
"activation_keywords": [
"release"
],
"is_enabled": true,
"author": "user",
"provenance": "authored",
"created_by": "user_example",
"created_at": "2026-09-01T00:00:00Z",
"updated_at": "2026-09-01T00:00:00Z"
}{
"error": "prompt_required"
}{
"error": "missing_bearer",
"message": "Authorization required"
}{
"error": "missing_scope",
"required_scope": "sessions:read"
}{
"error": {
"type": "rate_limited",
"message": "This API key exceeded its request-rate limit"
}
}Create a skill
Creates an authored workspace skill from non-empty name and instructions. description explains when to select it; activation_keywords are search aliases, not automatic triggers. Returns skill_id and provenance without running work. Oversized instructions return 413; disabled cloud-agent configuration returns 503.
Scope:skills:writecurl --request POST \
--url https://api.reasonmachines.com/v3/organizations/{orgId}/skills \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"name": "Release checklist",
"description": "Use when preparing a production release, deploying changes, or applying the team's release checklist.",
"instructions": "Run the full test suite, update the changelog, then wait for a human approval before deploying.",
"activation_keywords": [
"release",
"deploy"
]
}
EOFimport requests
url = "https://api.reasonmachines.com/v3/organizations/{orgId}/skills"
payload = {
"name": "Release checklist",
"description": "Use when preparing a production release, deploying changes, or applying the team's release checklist.",
"instructions": "Run the full test suite, update the changelog, then wait for a human approval before deploying.",
"activation_keywords": ["release", "deploy"]
}
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: 'Release checklist',
description: 'Use when preparing a production release, deploying changes, or applying the team\'s release checklist.',
instructions: 'Run the full test suite, update the changelog, then wait for a human approval before deploying.',
activation_keywords: ['release', 'deploy']
})
};
fetch('https://api.reasonmachines.com/v3/organizations/{orgId}/skills', 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}/skills",
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' => 'Release checklist',
'description' => 'Use when preparing a production release, deploying changes, or applying the team\'s release checklist.',
'instructions' => 'Run the full test suite, update the changelog, then wait for a human approval before deploying.',
'activation_keywords' => [
'release',
'deploy'
]
]),
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}/skills"
payload := strings.NewReader("{\n \"name\": \"Release checklist\",\n \"description\": \"Use when preparing a production release, deploying changes, or applying the team's release checklist.\",\n \"instructions\": \"Run the full test suite, update the changelog, then wait for a human approval before deploying.\",\n \"activation_keywords\": [\n \"release\",\n \"deploy\"\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.reasonmachines.com/v3/organizations/{orgId}/skills")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"name\": \"Release checklist\",\n \"description\": \"Use when preparing a production release, deploying changes, or applying the team's release checklist.\",\n \"instructions\": \"Run the full test suite, update the changelog, then wait for a human approval before deploying.\",\n \"activation_keywords\": [\n \"release\",\n \"deploy\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/organizations/{orgId}/skills")
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\": \"Release checklist\",\n \"description\": \"Use when preparing a production release, deploying changes, or applying the team's release checklist.\",\n \"instructions\": \"Run the full test suite, update the changelog, then wait for a human approval before deploying.\",\n \"activation_keywords\": [\n \"release\",\n \"deploy\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"skill_id": "skill_example",
"name": "Release checklist",
"description": "Use when preparing a release.",
"instructions": "Run the repository's release checks before deploying.",
"activation_keywords": [
"release"
],
"is_enabled": true,
"author": "user",
"provenance": "authored",
"created_by": "user_example",
"created_at": "2026-09-01T00:00:00Z",
"updated_at": "2026-09-01T00:00:00Z"
}{
"error": "prompt_required"
}{
"error": "missing_bearer",
"message": "Authorization required"
}{
"error": "missing_scope",
"required_scope": "sessions:read"
}{
"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
Display name for the authored skill.
Skill document containing instructions an agent should follow when using this skill.
Describe what the skill does and when an agent should use it. Reason uses this description for semantic selection.
Optional search aliases retained for compatibility. They improve explicit search but do not activate the skill automatically.
Whether agents may use the skill. Omit it to create the skill enabled; send false to save it without offering it to agents.
organization (the default) shares the skill with the Workspace. Legacy workspaces also accept personal; consolidated workspace resources return workspace_skill_scope_required for that scope.
personal, organization Response
Created.
Skill id; pass it as skillId to read, update or delete the skill.
Display name of the skill.
What the skill does and when an agent should use it. Reason uses this description for semantic selection.
The skill document an agent follows when it uses the skill.
Optional search aliases retained for compatibility. They do not activate the skill automatically.
Whether agents may use the skill.
Whether only its creator sees the skill, or the whole workspace.
personal, organization Who wrote the skill: user for one authored in the workspace, otherwise its publisher.
Where the skill came from, which decides what can be edited or deleted.
authored, skills_sh, git, agent_plugin, curated_integration, builtin User id of the member who added it; null when no member added it.
When the skill was added.
When the skill last changed.

