API Overview
The ArcSmith REST API provides local network endpoints to query, create, mutate, or trigger actions inside the story editor.
Server Address
The API server is hosted locally on the machine running the ArcSmith Tauri application. It binds strictly to the loopback interface:
http://127.0.0.1:14230
CORS Configuration
CORS is configured per-method to balance accessibility with safety:
| Methods | Access-Control-Allow-Origin | Notes |
GET, OPTIONS | * | Read-only — safe to expose broadly |
POST, PATCH, DELETE | null | Blocks cross-origin browser requests; curl/Python/Node.js are unaffected |
Startup Sync
GET /api/project waits up to 2 seconds for the editor to push its initial state into the API server. This ensures that agents polling immediately after launch always receive the real project, never an empty {}.
Important Note
The API server runs in a background thread inside the native Rust process. It is active as long as the ArcSmith desktop application window is open and running. If port 14230 is already in use, the editor will display an error notification.
Authentication
Details on security and authentication scopes for the ArcSmith local server.
Zero-Config Local Scopes
Since the API server binds exclusively to the local loopback address (127.0.0.1), all requests remain within your local environment. To keep the developer experience friction-free for script triggers and AI agents, **no tokens, API keys, or authentication headers are required**.
Security Boundaries
Because the server does not bind to public interface addresses (like 0.0.0.0), it is inaccessible from external networks. It is highly recommended to only run the app on secure developer nodes.
Core Models & Types
TypeScript definitions and schemas for the objects managed in ArcSmith.
Character Schema
| Field |
Type |
Required |
Description |
id |
string |
Yes |
Unique character identifier. |
name |
string |
Yes |
The display name of the character. |
portraits |
string[] |
Yes |
Filenames or asset paths for character poses/portraits. |
customProperties |
object |
No |
Key-value map for narrative properties (e.g. {"age": "24", "alignment": "good"}). |
Environment Schema
| Field |
Type |
Required |
Description |
id |
string |
Yes |
Unique environment identifier. |
name |
string |
Yes |
Name of the location. |
description |
string |
Yes |
Description text for reference. |
imagePath |
string |
No |
Concept art asset path for visual background previews. |
customProperties |
object |
No |
Metadata key-value properties. |
Variable Schema
| Field |
Type |
Required |
Description |
id |
string |
Yes |
Unique variable identifier. |
name |
string |
Yes |
Name referenced in logic checks (e.g., has_key). |
type |
'bool' | 'int' | 'str' |
Yes |
Data type for matching conditions. |
value |
boolean | number | string |
Yes |
Initial default value. |
ArcNode Schema
| Field |
Type |
Required |
Description |
id |
string |
Yes |
Unique identifier. |
type |
'dialogue' | 'monologue' | 'hub' | 'condition' | 'set' | 'jump' |
Yes |
Narrative node behavior block type. |
x |
number |
Yes |
X position coordinate on the flow grid. |
y |
number |
Yes |
Y position coordinate on the flow grid. |
sceneId |
string |
Yes |
The scene container the node lives in. |
data |
object |
Yes |
Contains dynamic properties based on the node's type. |
NodeData Fields
| Field |
Type |
Description |
title |
string |
Node display header name. |
text |
string |
Dialogue or Monologue narration text content. |
characterId |
string |
Character assigned to dialogue. |
portrait |
string |
Active character pose filename. |
environmentId |
string |
Linked location tag. |
audio |
string |
Audio cue asset path. |
variableId |
string |
Variable checked or modified (for Condition/Set nodes). |
conditionOperator |
'==' | '!=' | '>' | '<' | '>=' | '<=' |
Operator comparison used in Condition Node. |
conditionValue |
any |
Target comparative condition value. |
setOperator |
'=' | '+=' | '-=' | 'toggle' |
Operator used to mutate variable in Set Node. |
setValue |
any |
Mutation value. |
targetSceneId |
string |
Target Scene reference for Jump Node. |
imageAttachment |
string |
Concept art thumbnail path. |
LLM Integration Skill
A pre-formatted developer skill definition (SKILL.md) to integrate ArcSmith with agentic coding environments like Claude, Gemini, or custom LLM prompts.
Using the Skill
Save the following block as SKILL.md inside your AI assistant's skills library or include it directly in your custom agent instructions. It provides the structured schemas, cascading paradigms, and endpoint definitions optimized for agentic runtime execution.
SKILL.md Source
<!-- YAML Frontmatter -->
---
name: arcsmith-api
description: Automate, sync, and manipulate narrative graph structures and cast assets in the running ArcSmith editor via its background REST API. Trigger this skill whenever you need to fetch project states, programmatically update the canvas, manage sub-resources (characters, variables, environments, scenes, nodes, connections), or execute active editor actions (undo, redo, focus nodes, switch scenes).
---
# ArcSmith Editor API Integration Skill
Use this skill to interface with the local REST API server exposed by the running ArcSmith desktop Tauri application.
## 1. Server Metadata & Setup
- **Base URL:** http://127.0.0.1:14230
- **Authentication:** Zero-configuration (no keys or tokens required — binds strictly to loopback).
- **Schema Discovery:** GET /api/schema returns all endpoints at runtime.
## 2. CORS Policy
- GET / OPTIONS: Access-Control-Allow-Origin: * (read-only, safe to expose)
- POST / PATCH / DELETE: Access-Control-Allow-Origin: null (blocks cross-origin browser requests; curl/Python/Node.js unaffected)
## 3. API Endpoints
### Health
- **GET /health** — Returns {"status":"ok","version":"1.0.1","service":"ArcSmith"}
- **GET /api/schema** — Machine-readable endpoint listing
### Narrative Project
- **GET /api/project** — Fetch the full project JSON. Waits up to 2s for startup sync.
- **POST /api/project** — Replace the entire project state. Body: application/json.
### Sub-Resources CRUD
:resource = characters | environments | variables | scenes | nodes | connections
- **GET /api/:resource** — List all items (returns {} or [] if key absent, never 500).
- **POST /api/:resource** — Create/upsert item. ID auto-generated if omitted. Injects default fields.
- **GET /api/:resource/:id** — Fetch single item or 404.
- **PATCH /api/:resource/:id** — Shallow-merge partial fields onto existing item. Body: application/json.
- **DELETE /api/:resource/:id** — Remove item with full cascade cleanup.
- Cannot delete the last scene (returns 409 Conflict).
- Scenes: clears entryNodeId + resets activeSceneId to first remaining scene.
### Editor Action Commands
- **GET /api/actions** — Returns {"allowed_actions": [...]}
- **POST /api/actions** — Triggers an editor operation:
- {"action": "undo"} -> Revert last change (Ctrl+Z)
- {"action": "redo"} -> Re-apply last change (Ctrl+Y)
- {"action": "new_project"} -> Reset to clean canvas
- {"action": "select_node", "payload": {"nodeId": "id" | null}} -> Highlight node
- {"action": "set_active_scene", "payload": {"sceneId": "id"}} -> Switch scene tab
- Unknown action names return 400 with the allowed list.
## 4. Data Schemas (Sub-Resources)
- **Character:** {"id": string, "name": string, "portraits": string[], "customProperties"?: object}
- **Environment:** {"id": string, "name": string, "description": string, "imagePath"?: string, "customProperties"?: object}
- **Variable:** {"id": string, "name": string, "type": "bool"|"int"|"str", "value": any}
- **Scene:** {"id": string, "name": string, "color": string}
- **ArcNode:** {"id": string, "type": "dialogue"|"monologue"|"hub"|"condition"|"set"|"jump", "x": number, "y": number, "sceneId": string, "data": object}
- type, x, y, sceneId are REQUIRED for POST — returns 422 if missing.
- **ArcConnection:** {"id": string, "fromNode": string, "fromSlot": string, "toNode": string, "toSlot": string}
## 5. HTTP Status Codes Used
- 200 OK — Success
- 400 Bad Request — Malformed JSON or bad field values
- 404 Not Found — Resource or ID not found
- 405 Method Not Allowed — Includes Allow header (RFC 7231)
- 409 Conflict — Cannot delete last scene
- 413 Content Too Large — Body exceeds 20 MB
- 415 Unsupported Media Type — Missing Content-Type: application/json
- 422 Unprocessable Entity — Valid JSON but missing required node fields
- 500 Internal Server Error — Mutex poisoned or serialisation failure
- 503 Service Unavailable — Too many concurrent connections (limit: 32)
Get Project State
Retrieve the complete project schema containing all node networks, variables, environments, characters, and scenes.
GET
/api/project
Response Status Codes
| Status |
Description |
200 OK |
Request succeeded. Returns full JSON project tree payload. |
Example Code Codeblocks
curl http://localhost:14230/api/project
const res = await fetch('http://localhost:14230/api/project');
const project = await res.json();
console.log(project);
Invoke-RestMethod -Uri http://localhost:14230/api/project
Update Project State
Override the entire document state instantly and sync updates directly with the running editor client.
POST
/api/project
Request Body Headers
Must send header: Content-Type: application/json. The body payload must be a complete ProjectData structure matching the core schemas.
Response Status Codes
| Status |
Description |
200 OK |
Successfully parsed, loaded into Zustand, and committed to layout. |
400 Bad Request |
Invalid JSON payload structure or malformed document object. |
Example Code Codeblocks
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"My Story","description":"","entryNodeId":null,"characters":{},"environments":{},"variables":{},"scenes":{},"nodes":{},"connections":[],"assets":[]}' \
http://localhost:14230/api/project
const payload = {
title: "My Story",
description: "",
entryNodeId: null,
characters: {},
environments: {},
variables: {},
scenes: {},
nodes: {},
connections: [],
assets: []
};
const res = await fetch('http://localhost:14230/api/project', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
Invoke-RestMethod -Uri http://localhost:14230/api/project -Method Post -Body '{"title":"My Story","description":"","entryNodeId":null,"characters":{},"environments":{},"variables":{},"scenes":{},"nodes":{},"connections":[],"assets":[]}' -ContentType "application/json"
Get Resource Map
Retrieve the complete map or list for a specific sub-resource type.
GET
/api/:resource
Path Parameters
| Parameter |
Accepted Values |
Description |
:resource |
characters | environments | variables | scenes | nodes | connections |
The specific schema registry category to request. |
Example Code Codeblocks
curl http://localhost:14230/api/characters
const res = await fetch('http://localhost:14230/api/characters');
const characters = await res.json();
console.log(characters);
Invoke-RestMethod -Uri http://localhost:14230/api/characters
Get Single Resource Item
Fetch a single sub-resource item by its unique ID. Returns 404 Not Found if the ID does not exist.
GET
/api/:resource/:id
Path Parameters
| Parameter | Accepted Values | Description |
:resource | characters | environments | variables | scenes | nodes | connections | Resource category. |
:id | string | The unique ID of the item to retrieve. |
Response Status Codes
| Status | Description |
200 OK | Item found. Returns the single JSON object. |
400 Bad Request | Invalid ID format. |
404 Not Found | No item with that ID exists. |
Example Code
curl http://localhost:14230/api/characters/18bc7557258d3cec
const res = await fetch('http://localhost:14230/api/characters/18bc7557258d3cec');
const character = await res.json();
console.log(character);
Invoke-RestMethod -Uri http://localhost:14230/api/characters/18bc7557258d3cec
Add / Update Sub-Resource
Create a new sub-resource element or update an existing element by providing its unique identifier.
POST
/api/:resource
Path Parameters
| Parameter |
Accepted Values |
Description |
:resource |
characters | environments | variables | scenes | nodes | connections |
Target category. |
Request Body rules
The request body payload must represent a single item. If the body includes an id field, the API updates the existing matching resource. If id is omitted, a unique identifier is generated automatically, and the newly created object (including its generated id) is returned in the response.
Example Code Codeblocks
curl -X POST \
-H "Content-Type: application/json" \
-d '{"name":"Alistair","portraits":["pose1.png"]}' \
http://localhost:14230/api/characters
const characterData = {
name: "Alistair",
portraits: ["pose1.png"]
};
const res = await fetch('http://localhost:14230/api/characters', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(characterData)
});
Invoke-RestMethod -Uri http://localhost:14230/api/characters -Method Post -Body '{"name":"Alistair","portraits":["pose1.png"]}' -ContentType "application/json"
Partial Update Sub-Resource
Merge a partial JSON payload onto an existing resource item. Only the fields you include are changed — all other fields are preserved. Returns the updated item.
PATCH
/api/:resource/:id
Path Parameters
| Parameter | Accepted Values | Description |
:resource | characters | environments | variables | scenes | nodes | connections | Resource category. |
:id | string | The unique ID of the item to update. |
Request Body
Must include Content-Type: application/json. The body is a partial object whose fields are shallow-merged onto the existing item.
Response Status Codes
| Status | Description |
200 OK | Returns the full updated item after merging. |
400 Bad Request | Invalid JSON body or invalid ID format. |
404 Not Found | No item with that ID exists. |
415 Unsupported Media Type | Content-Type was not application/json. |
Example Code
curl -X PATCH \
-H "Content-Type: application/json" \
-d '{"name":"Alistair Thorne"}' \
http://localhost:14230/api/characters/18bc7557258d3cec
const res = await fetch('http://localhost:14230/api/characters/18bc7557258d3cec', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: "Alistair Thorne" })
});
const updated = await res.json();
Invoke-RestMethod -Uri http://localhost:14230/api/characters/18bc7557258d3cec -Method Patch -Body '{"name":"Alistair Thorne"}' -ContentType "application/json"
Delete Sub-Resource
Delete a resource by ID and automatically trigger cascading references cleanup to maintain graph integrity.
DELETE
/api/:resource/:id
Path Parameters
| Parameter |
Accepted Values |
Description |
:resource |
characters | environments | variables | scenes | nodes | connections |
Target resource category. |
:id |
string |
Unique ID of the specific item to delete. |
Automatic Cascading Cleanups
References Integrity Cascade
Deleting a resource automatically triggers reference cleanups inside the project JSON:
- Characters: Wipes
characterId and portrait references in all dialogue nodes.
- Environments: Wipes
environmentId references in all dialogue/monologue nodes.
- Variables: Wipes
variableId references in all condition and set nodes.
- Nodes: Removes any connection involving the deleted node. Resets
entryNodeId to null if matched.
- Scenes: Wipes all nodes inside the scene, cleans their associated connections, resets
entryNodeId if it pointed into the scene, and resets activeSceneId to the first remaining scene. Cannot delete the last scene — returns 409 Conflict.
Example Code Codeblocks
curl -X DELETE http://localhost:14230/api/characters/18bc7557258d3cec
const res = await fetch('http://localhost:14230/api/characters/18bc7557258d3cec', {
method: 'DELETE'
});
Invoke-RestMethod -Uri http://localhost:14230/api/characters/18bc7557258d3cec -Method Delete
List Valid Editor Actions
Returns the complete list of action names accepted by POST /api/actions. Useful for agents that need to discover the action vocabulary at runtime.
GET
/api/actions
Response
Returns a JSON object with an allowed_actions array:
{
"allowed_actions": [
"undo", "redo", "new_project", "select_node", "set_active_scene"
]
}
Execute Editor Actions
Instruct the running desktop UI editor to execute core actions like undo, redo, scene selection, or node selection.
POST
/api/actions
Supported Actions and Payload Specs
The request body must be a JSON object with an action field. Below are the supported actions:
| Action |
Payload Object |
Description |
undo |
None |
Revert the last editor modification. Matches pressing Ctrl + Z. |
redo |
None |
Re-apply the next editor modification. Matches pressing Ctrl + Y. |
new_project |
None |
Clear the workspace canvas and initialize a blank new project. |
select_node |
{"nodeId": "someNodeId" | null} |
Highlight and focus a specific node on the canvas. Use null to clear. |
set_active_scene |
{"sceneId": "someSceneId"} |
Switch the active tab view to another scene container. |
Example Code Codeblocks
curl -X POST \
-H "Content-Type: application/json" \
-d '{"action":"undo"}' \
http://localhost:14230/api/actions
const payload = {
action: "select_node",
payload: { nodeId: "18bc7557258d3cec" }
};
const res = await fetch('http://localhost:14230/api/actions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
Invoke-RestMethod -Uri http://localhost:14230/api/actions -Method Post -Body '{"action":"undo"}' -ContentType "application/json"
Error Response for Unknown Actions
If the action value is not in the allowed list, the server returns 400 Bad Request with the list of valid actions:
{
"status": "error",
"message": "Unknown action 'fly_away'. See allowed_actions.",
"allowed_actions": ["undo", "redo", "new_project", "select_node", "set_active_scene"]
}
API Schema Discovery
Returns a machine-readable JSON document listing all available endpoints, their methods, and descriptions. Ideal for agents bootstrapping their API knowledge at runtime.
GET
/api/schema
Example Code
curl http://localhost:14230/api/schema
const res = await fetch('http://localhost:14230/api/schema');
const schema = await res.json();
console.log(schema.endpoints);
Invoke-RestMethod -Uri http://localhost:14230/api/schema