curl --request POST \
--url https://api.reasonmachines.com/v3/feedback \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"category": "docs_mismatch",
"note": "listSessions returned next_cursor; docs say cursor.",
"session_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://api.reasonmachines.com/v3/feedback"
payload = {
"category": "docs_mismatch",
"note": "listSessions returned next_cursor; docs say cursor.",
"session_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
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({
category: 'docs_mismatch',
note: 'listSessions returned next_cursor; docs say cursor.',
session_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://api.reasonmachines.com/v3/feedback', 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/feedback",
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([
'category' => 'docs_mismatch',
'note' => 'listSessions returned next_cursor; docs say cursor.',
'session_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
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/feedback"
payload := strings.NewReader("{\n \"category\": \"docs_mismatch\",\n \"note\": \"listSessions returned next_cursor; docs say cursor.\",\n \"session_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\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/feedback")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"category\": \"docs_mismatch\",\n \"note\": \"listSessions returned next_cursor; docs say cursor.\",\n \"session_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/feedback")
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 \"category\": \"docs_mismatch\",\n \"note\": \"listSessions returned next_cursor; docs say cursor.\",\n \"session_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"submitted_at": "2026-09-27T12:00:00.000Z",
"feedback_id": "6f1c2a4e-8d3b-4f5a-9c7e-1b2d3e4f5a6b"
}{
"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"
}
}Report agent feedback
Tell the Reason team when something gets in your way: a response that is wrong or differs from these docs, an unexpected error, or a missing capability. Send one report per distinct problem. Any valid key can report, whatever its scopes.
Add this to your agent’s instructions
If the Reason API is wrong, surprising,
undocumented, or blocks your task, report
it with POST /v3/feedback. Include the
session_id if any. Never include secrets.
Retries and report fields
Make a submission_id (a UUID) once per report: a retry with the same id returns the report already received, with duplicate: true, and sends nothing twice. title names the problem in one line, and wants_response asks the team to reply.
curl --request POST \
--url https://api.reasonmachines.com/v3/feedback \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"category": "docs_mismatch",
"note": "listSessions returned next_cursor; docs say cursor.",
"session_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
'import requests
url = "https://api.reasonmachines.com/v3/feedback"
payload = {
"category": "docs_mismatch",
"note": "listSessions returned next_cursor; docs say cursor.",
"session_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
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({
category: 'docs_mismatch',
note: 'listSessions returned next_cursor; docs say cursor.',
session_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a'
})
};
fetch('https://api.reasonmachines.com/v3/feedback', 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/feedback",
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([
'category' => 'docs_mismatch',
'note' => 'listSessions returned next_cursor; docs say cursor.',
'session_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a'
]),
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/feedback"
payload := strings.NewReader("{\n \"category\": \"docs_mismatch\",\n \"note\": \"listSessions returned next_cursor; docs say cursor.\",\n \"session_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\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/feedback")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"category\": \"docs_mismatch\",\n \"note\": \"listSessions returned next_cursor; docs say cursor.\",\n \"session_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/feedback")
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 \"category\": \"docs_mismatch\",\n \"note\": \"listSessions returned next_cursor; docs say cursor.\",\n \"session_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"submitted_at": "2026-09-27T12:00:00.000Z",
"feedback_id": "6f1c2a4e-8d3b-4f5a-9c7e-1b2d3e4f5a6b"
}{
"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, devices:read, devices:write, devices:use, computers:read, computers:write, ssh:write, knowledge:read, memory:read, memory:write, skills:read, skills:write, pages:read, pages: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. devices:write, devices:use, computers:read, computers:write, ssh:write, network:read and network:write are opt-in, excluded from default keys. computers:read lists visible cloud and connected computers without waking them or granting execution access. devices:read discovers local Device targets; devices:use separately consents to local execution, so a discover-and-run local workflow needs both. devices:write manages enrollment and device grants; computers:write creates or deletes cloud computers; ssh:write manages retained SSH access. 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).
Body
How the Reason team routes the report: pick the value closest to the main problem, such as docs_mismatch when a response differs from the documentation, feature_gap when a missing capability blocked the task, or model_behavior when an agent's own conduct went wrong.
bug, docs_mismatch, friction, feature_gap, model_behavior, other Free-text account of the problem: the call made, what was expected and what happened instead, with any error code. Leave out secrets, credentials and personal data.
1 - 4000The affected Session ID from listSessions or createSession; omit for a report unrelated to a Session.
^([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)$One line naming the problem, shown as the report's heading.
200True when the sender would like the Reason team to reply.
Your own id for this report, generated once. Retrying with the same submission_id returns the report already received instead of sending it twice.
^([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)$Response
The report was received.
True when the report reached the Reason team; a report that could be neither stored nor delivered returns 502 instead.
When Reason received the report.
The stored report's ID, for referring to it with the Reason team. Absent when the report reached the team without being stored.
^([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)$Present when a report with this submission_id was already received; nothing new was sent.

