curl --request POST \
--url https://api.reasonmachines.com/v3/organizations/{orgId}/pages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"html": "<string>",
"reason_id": "<string>",
"session_id": "<string>",
"height": 123
}
'import requests
url = "https://api.reasonmachines.com/v3/organizations/{orgId}/pages"
payload = {
"title": "<string>",
"html": "<string>",
"reason_id": "<string>",
"session_id": "<string>",
"height": 123
}
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({
title: '<string>',
html: '<string>',
reason_id: '<string>',
session_id: '<string>',
height: 123
})
};
fetch('https://api.reasonmachines.com/v3/organizations/{orgId}/pages', 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}/pages",
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([
'title' => '<string>',
'html' => '<string>',
'reason_id' => '<string>',
'session_id' => '<string>',
'height' => 123
]),
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}/pages"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"html\": \"<string>\",\n \"reason_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"height\": 123\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}/pages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"html\": \"<string>\",\n \"reason_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"height\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/organizations/{orgId}/pages")
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 \"title\": \"<string>\",\n \"html\": \"<string>\",\n \"reason_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"height\": 123\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"title": "<string>",
"revision": 123,
"kind": "page",
"session_id": "<string>",
"url": "<string>",
"view_url": "<string>",
"view_url_expires_at": "2023-11-07T05:31:56Z",
"preview": {
"light": "<string>",
"dark": "<string>",
"width": 123,
"height": 123,
"hero": true
},
"preview_capture": "scheduled",
"share": {
"visibility": "private",
"url": "<string>",
"can_manage": true
},
"source": "<string>"
}{
"error": "missing_bearer",
"message": "Authorization required"
}{
"error": {
"type": "rate_limited",
"message": "This API key exceeded its request-rate limit"
}
}Create a page
Makes a page from html and title, checked as the agent’s app_show checks one: a live page’s handlers must be able to run, and the page may name no local file (inline images as data: URIs or use https URLs). It lives in the conversation of reason_id (your main Reason when neither reason_id nor session_id is given), so it shows in that Reason’s Library, or in Session session_id, which you may read. You own it: only you share, rename or delete it. To change it later, call updatePage with its id rather than making another page. The hero picture is taken now when the page’s Session has a running turn of yours (preview_capture); otherwise the Library renders the page live. Requires pages:write and a member who may write.
pages:writecurl --request POST \
--url https://api.reasonmachines.com/v3/organizations/{orgId}/pages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"html": "<string>",
"reason_id": "<string>",
"session_id": "<string>",
"height": 123
}
'import requests
url = "https://api.reasonmachines.com/v3/organizations/{orgId}/pages"
payload = {
"title": "<string>",
"html": "<string>",
"reason_id": "<string>",
"session_id": "<string>",
"height": 123
}
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({
title: '<string>',
html: '<string>',
reason_id: '<string>',
session_id: '<string>',
height: 123
})
};
fetch('https://api.reasonmachines.com/v3/organizations/{orgId}/pages', 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}/pages",
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([
'title' => '<string>',
'html' => '<string>',
'reason_id' => '<string>',
'session_id' => '<string>',
'height' => 123
]),
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}/pages"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"html\": \"<string>\",\n \"reason_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"height\": 123\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}/pages")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"html\": \"<string>\",\n \"reason_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"height\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.reasonmachines.com/v3/organizations/{orgId}/pages")
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 \"title\": \"<string>\",\n \"html\": \"<string>\",\n \"reason_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"height\": 123\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"title": "<string>",
"revision": 123,
"kind": "page",
"session_id": "<string>",
"url": "<string>",
"view_url": "<string>",
"view_url_expires_at": "2023-11-07T05:31:56Z",
"preview": {
"light": "<string>",
"dark": "<string>",
"width": 123,
"height": 123,
"hero": true
},
"preview_capture": "scheduled",
"share": {
"visibility": "private",
"url": "<string>",
"can_manage": true
},
"source": "<string>"
}{
"error": "missing_bearer",
"message": "Authorization required"
}{
"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).
Path Parameters
Organization id or slug. Resolve it with GET /v3/self.
Body
The page's name, 1 to 200 characters.
One self-contained HTML document. A page with a chart opens with <figure data-page-hero> (the pages skill's hero rule). Local file paths are not readable here: inline images as data: URIs (the reason CLI does this for files on its machine) or use https URLs.
The Reason whose conversation keeps the page, so it shows in that Reason's Library. Omit both this and session_id for your own main Reason.
A Session you may use to keep the page instead of a Reason's conversation.
The frame height in the chat, in CSS pixels; omit it to fit the page.
Response
The page; the same page when the same title and HTML were already saved in that Session.
The page's id, from listPages, createPage or a page link's page= parameter.
The page's name.
1 when made, one more for each update under the same id.
page for an HTML page; document for a Markdown document page, which this API reads but does not write.
page, document The Session the page lives in.
The page's link in Reason: its Session opened on the page, for anyone who may read that Session. It never changes.
A short-lived URL that serves the page itself for the caller (sandboxed HTML); null for a document.
When view_url stops loading.
The hero picture of the current revision; null until one is captured (the Library then renders the page live).
Show child attributes
Show child attributes
On create and update: scheduled when the page's Session has a running turn of yours whose machine takes the hero picture now; not_running when it has none, so the Library renders the page live until a later turn shows it.
scheduled, not_running Show child attributes
Show child attributes
With include=source: the page's HTML as written (local images inlined), or a document's Markdown.

