πŸ”Œ API Reference

Read and write to your trees programmatically. Build integrations, automations, bots, and external tools on the same backend that powers the TreeOS apps.

πŸ”‘ Overview & Authentication
The TreeOS API lives at https://treeos.ai/api/v1. All endpoints return JSON by default. Every write operation and most read operations require authentication.

API Keys are the recommended way to authenticate for programmatic access. Create and manage keys from your user profile page. Keys are sent as a request header:
x-api-key: YOUR_API_KEY
Key details:
β€’ Created and managed from your profile page
β€’ Each key can be individually revoked
β€’ Usage is tracked per key
β€’ Maximum of 10 active keys per user
β€’ Works with all endpoints (read and write)
πŸ” Authentication
Register, login, and logout. Returns a JWT token for subsequent API calls. Pass the token as Authorization: Bearer TOKEN or as a cookie. API keys can also be used via x-api-key header.
POST/api/v1/register
Create a new account. Body: { username, password }. Returns JWT token.
POST/api/v1/login
Authenticate. Body: { username, password }. Returns JWT token and user info.
POST/api/v1/logout
Clear session cookie. Requires authentication.
πŸ‘‹ Identity (Me)
Use this endpoint to verify your API key and discover your userId. This is the first call most integrations make.
GET/api/v1/me
Returns your identity, plan, energy, and share token.
Response
{ "success": true, "userId": "abc-123", "username": "tabor", "isAdmin": true, "plan": "premium", "planExpiresAt": "2026-12-31T...", "email": "you@email.com", "shareToken": "your-share-token", "storageUsageMb": 12.5, "energy": { "available": 87, "additional": 100, "total": 187 } }
Generate API keys from your profile page at /api/v1/user/:userId/api-keys?html. You can have up to 10 active keys and revoke them individually.
🧠 Tree Chat
The highest-level endpoint for interacting with a tree. Send a message to any tree and get a natural language response back. It distills every other function (navigation, placement, notes, structure, queries) into a single call β€” send context in, build and read the tree, get a response out.

The AI walks the tree, finds where your idea belongs (or gathers context for a question), executes the operations, and returns a conversational answer. It works with whatever root you give it.
POST/api/v1/root/:rootId/chat
Send a message to a tree. The AI reads the tree, places ideas or answers questions, and returns a response.
Request Body
{ "message": "flights seem cheaper in late March" }
Response
{ "success": true, "answer": "Noted β€” added that to your flight planning." }
Works for both placing information and asking questions. The response is always natural language β€” no tree internals are exposed.
πŸ“Œ Tree Place
Place content onto a tree without generating a conversational response. The AI navigates the tree, finds where the idea belongs, and executes the operations β€” but skips the final response step, making it faster and cheaper than Tree Chat.

Returns structured results (what was placed and where) instead of natural language. Use Tree Chat above if you need a conversational reply.
POST/api/v1/root/:rootId/place
Place content onto a specific tree. The AI navigates, creates structure, and edits nodes β€” but does not generate a response.
Request Body
{ "message": "flights seem cheaper in late March" }
Response
{ "success": true, "stepSummaries": [...], "targetNodeId": "abc123", "targetPath": "Trip / Flights" }
Same orchestration as Tree Chat, minus the final response generation. Useful for bulk ingestion or automated pipelines where you don't need a conversational reply.
πŸ” Tree Query
Talk to your tree without it making any changes. The AI reads the tree and generates a response, but will never create, edit, or delete nodes. Useful for asking questions, getting summaries, or exploring what's in your tree without risk of modification.
POST/api/v1/root/:rootId/query
Query a tree in read-only mode. The AI reads the tree and responds but cannot make any edits.
Request Body
{ "message": "what are my top priorities right now?" }
Response
{ "success": true, "answer": "Based on your tree, your top priorities are..." }
Same orchestration as Tree Chat, but the classifier intent is forced to query-only. Even if the message sounds like a placement request, the AI will only respond with information and never edit the tree. On public trees, this endpoint works without authentication if the tree owner has an LLM assigned to the placement slot (owner pays energy). Authenticated visitors from other lands can also query using their own LLM via canopy proxy.
GET/api/v1/root/:rootId/query?html
Renders a lightweight query page for interacting with the tree. On public trees, anyone can access this page without authentication.
Requires ENABLE_FRONTEND_HTML=true. The page provides a simple chat interface for sending queries and viewing responses. No tree editing, no mode switching.
πŸ‘€ User Endpoints
Profile, roots, API keys, contributions, notes, tags, raw ideas, invites, chat history, and custom LLM configuration.

Additional user endpoints are added by extensions: energy, raw-ideas, api-keys, deleted-revive, llm-failover, user-queries.

Profile
GET/api/v1/user/:userId
Returns user profile, root nodes, and account metadata.
Create Root
POST/api/v1/user/:userId/createRoot
Create a new root tree for the user.
Request Body
{ "name": "My New Tree" }
API Keys
POST/api/v1/user/:userId/api-keys
Create a new API key.
Request Body
{ "name": "optional descriptive name" }
Response
{ "apiKey": "fcd8a7c7...", "message": "Store this key securely. You will not see it again." }
The full key is only shown once at creation time.
GET/api/v1/user/:userId/api-keys
List all API keys (active and revoked) for the user.
DELETE/api/v1/user/:userId/api-keys/:keyId
Revoke an API key. It will no longer authenticate requests.
Share Token
The URL token used for ?token= authentication on GET routes.
GET/api/v1/user/:userId/shareToken
View share token page (?html) or get token as JSON. URL auth (share token or JWT).
POST/api/v1/user/:userId/shareToken
Update share token. Body: { htmlShareToken }. Owner only.
Contributions
GET/api/v1/user/:userId/contributions
All contributions made by the user across all trees.
Query Parameters
?limit=NUMBERMax results to return
?startDate=YYYY-MM-DDFilter from date
?endDate=YYYY-MM-DDFilter to date
Notes
GET/api/v1/user/:userId/notes
List or search all notes posted by the user.
Query Parameters
?q=SEARCHFull-text search query
?limit=NUMBERMax results to return
?startDate=YYYY-MM-DDFilter from date
?endDate=YYYY-MM-DDFilter to date
Tagged Notes (Inbox)
GET/api/v1/user/:userId/tags
Notes where the user has been @tagged by another user.
Raw Ideas
Unstructured inputs (text or files) that can later be converted into notes and placed into a tree.
POST/api/v1/user/:userId/raw-ideas
Create a new raw idea. Accepts multipart form data.
Form Fields
content: "text string" file: (optional file upload)
GET/api/v1/user/:userId/raw-ideas
List or search raw ideas. Defaults to pending ideas.
Query Parameters
?status=VALUEFilter by status. One of: pending (default), processing, succeeded, stuck, deferred, deleted, all
?q=SEARCHFull-text search query (searches within content)
?limit=NUMBERMax results to return (max 200, default 200)
?startDate=YYYY-MM-DDFilter from date
?endDate=YYYY-MM-DDFilter to date
Succeeded ideas are sorted by placement date (placedAt) descending. All others sort by creation date.
GET/api/v1/user/:userId/raw-ideas/:rawIdeaId
View a single raw idea including its status and AI session link.
POST/api/v1/user/:userId/raw-ideas/:rawIdeaId/transfer
Manually convert a raw idea into a note on a target node.
Request Body
{ "nodeId": "targetNodeId" }
Returns 409 if the idea is currently being processed by AI.
DELETE/api/v1/user/:userId/raw-ideas/:rawIdeaId
Permanently delete a raw idea.
Returns 409 if the idea has status processing or succeeded.
POST/api/v1/user/:userId/raw-ideas/auto-place
Toggle automatic raw idea placement. When enabled, pending ideas are placed every 15 minutes while you are offline.
Request Body
{ "enabled": true }
Response
{ "success": true, "enabled": true }
Requires Standard, Premium, or God plan. Returns 403 for Basic tier users.
Deleted Branches
Deleted branches are soft-deleted and can be restored.
GET/api/v1/user/:userId/deleted
List all soft-deleted branches for the user.
POST/api/v1/user/:userId/deleted/:nodeId/revive
Restore a deleted branch under a parent node.
Request Body
{ "targetParentId": "nodeId" }
POST/api/v1/user/:userId/deleted/:nodeId/reviveAsRoot
Restore a deleted branch as a new root tree.
Invites & Collaboration
GET/api/v1/user/:userId/invites
List pending invitations to collaborate on other users' trees.
POST/api/v1/user/:userId/invites/:inviteId
Accept or decline a collaboration invite.
Request Body
{ "accept": true } // true to accept, false to decline
Energy
GET/api/v1/user/:userId/energy
View current energy balance, plan tier, profile type, and custom LLM connection status.
Custom LLM Connections
Connect your own LLM providers for AI features. Each user can have up to 15 connections and assign them to different slots. Any OpenAI-compatible endpoint works.
GET/api/v1/user/:userId/custom-llm
List all custom LLM connections for the user.
Response
{ "success": true, "connections": [ { "_id": "...", "name": "GPT-4o", "baseUrl": "https://api.openai.com/v1", "model": "gpt-4o", ... } ] }
POST/api/v1/user/:userId/custom-llm
Create a new custom LLM connection.
Request Body
{ "name": "My GPT-4o", "baseUrl": "https://api.openai.com/v1", "apiKey": "sk-...", "model": "gpt-4o" }
Max 15 connections per user. API key is encrypted at rest.
PUT/api/v1/user/:userId/custom-llm/:connectionId
Update an existing connection's name, URL, key, or model.
Request Body
{ "name": "Updated Name", "baseUrl": "https://api.openai.com/v1", "apiKey": "sk-new-key", "model": "gpt-4o-mini" }
Only apiKey and name are optional β€” omit apiKey to keep the existing key.
DELETE/api/v1/user/:userId/custom-llm/:connectionId
Permanently delete a connection. Auto-removes it from all user slots and root tree assignments.
POST/api/v1/user/:userId/llm-assign
Assign a connection to a user-level LLM slot, or unassign by passing null.
Request Body
{ "slot": "main", "connectionId": "connection-id-here" }
Allowed Slots
main β€” Default slot used for tree chat/profile rawIdea β€” Used for raw idea auto-placement
Pass connectionId: null to unassign the slot.
GET/api/v1/user/:userId/llm-failover
Get the user's LLM failover chain (ordered list of backup connections).
POST/api/v1/user/:userId/llm-failover
Push a connection to the failover chain. Body: { connectionId }.
DELETE/api/v1/user/:userId/llm-failover/:connectionId
Remove a specific connection from the failover chain.
DELETE/api/v1/user/:userId/llm-failover
Clear the entire failover chain.
AI Chats
GET/api/v1/user/:userId/chats
View AI chat history for a user, grouped by session.
Query Parameters
?limit=NUMBERMax sessions to return (default 10, max 10)
?sessionId=IDFilter to a specific session
?startDate=ISOFilter chats after this date
?endDate=ISOFilter chats before this date
Notifications
Notifications are generated automatically after tree dreams complete. Each dream produces a summary and a thought for the tree owner and contributors.
GET/api/v1/user/:userId/notifications
List notifications for the user, newest first.
Query Parameters
?rootId=IDFilter to a specific tree
?limit=NUMBERMax results (default 20, max 100)
?offset=NUMBERSkip N results for pagination
Response
{ "notifications": [ { "type": "dream-summary", "title": "...", "content": "...", "rootId": "...", "createdAt": "..." }, { "type": "dream-thought", "title": "...", "content": "...", "rootId": "...", "createdAt": "..." } ], "total": 12, "limit": 20, "offset": 0 }
Types: dream-summary (recap of what the dream did) and dream-thought (a reflective insight based on the dream activity).
🌳 Root Endpoints
Roots are the top-level entry point of a tree. Most tree-wide operations happen at this scope.

Additional root endpoints are added by extensions: values, dreams, holdings, schedules, calendar, understandings, gateway, book.

Tree Data
GET/api/v1/root/:rootId
Tree structure: node names, types, hierarchy, and status. Lightweight, no notes or contributions.
Query Parameters
?active=true|falseFilter by active status
?completed=true|falseFilter by completed status
?trimmed=true|falseFilter by trimmed status
GET/api/v1/root/:rootId/all
Full tree dump including notes, contributions, metadata, and scripts for every node.
Global Values
GET/api/v1/root/:rootId/values
All values aggregated across every child node in the tree.
Contributors & Ownership
POST/api/v1/root/:rootId/invite
Invite a collaborator to the tree.
Request Body
{ "userReceiving": "username-or-userId" }
POST/api/v1/root/:rootId/transfer-owner
Transfer tree ownership to another contributor.
Request Body
{ "userReceiving": "username-or-userId" }
POST/api/v1/root/:rootId/remove-user
Remove a contributor from the tree.
Request Body
{ "userReceiving": "username-or-userId" }
POST/api/v1/root/:rootId/retire
Archive the root (soft delete entire tree). Only the owner can do this when there are no other contributors.
Visibility
POST/api/v1/root/:rootId/visibility
Set tree visibility. Public trees can be browsed and queried by anyone without authentication. Owner only.
Request Body
{ "visibility": "public" }
Allowed Values
private β€” Only owner and invited contributors can access (default) public β€” Anyone can browse and query the tree
When public, if the tree has an LLM assigned to the placement slot, anonymous visitors can query it (owner pays energy). If no LLM is assigned, only authenticated visitors with their own LLM can query.
Transaction Policy
POST/api/v1/root/:rootId/transaction-policy
Set the approval policy for transactions on this tree.
Request Body
{ "policy": "OWNER_ONLY" }
Allowed Values
OWNER_ONLY β€” Only the tree owner can approve ANYONE β€” Any contributor can approve MAJORITY β€” Majority of contributors must approve ALL β€” All contributors must approve
Tree AI Models
POST/api/v1/root/:rootId/llm-assign
Assign a custom LLM connection to a tree slot. Owner-only, requires paid plan.
Request Body
{ "slot": "placement", "connectionId": "connection-id-here" }
Allowed Slots
placement β€” Tree chat, navigation, node placement, classification understanding β€” Understanding runs (falls back to placement) respond β€” Final user-facing responses (falls back to placement) notes β€” Note creation and editing (falls back to placement) cleanup β€” Cleanup analysis and expansion (falls back to placement) drain β€” Short-term memory drain placement (falls back to placement) notification β€” Dream notification summary and thought (falls back to placement)
Pass connectionId: null to unassign and revert to the user's default. The connection must belong to the root owner. Unset slots fall back to placement, then to the user's default LLM.
Dream Time
POST/api/v1/root/:rootId/dream-time
Set or clear the nightly dream schedule for a tree. When set, the tree will automatically run cleanup and understanding processes at the specified time. Owner-only.
Request Body
{ "dreamTime": "03:00" }
Use 24-hour HH:MM format. Pass null or omit to disable dreaming.
GET/api/v1/root/:rootId/holdings
List deferred items (short-term memory) for a tree. These are ideas the AI held back for better placement later.
GET/api/v1/root/:rootId/holdings/:itemId
View details of a specific deferred item.
POST/api/v1/root/:rootId/holdings/:itemId/dismiss
Dismiss a deferred item (mark as handled without placing).
AI Chats
GET/api/v1/root/:rootId/chats
View all AI chat sessions across the entire tree and its descendants.
Query Parameters
?limit=NUMBERMax sessions to return (default 10, max 10)
?sessionId=IDFilter to a specific session
?startDate=ISOFilter chats after this date
?endDate=ISOFilter chats before this date
Calendar
GET/api/v1/root/:rootId/calendar
All scheduled dates across every node in the tree.
Query Parameters
?month=0–11Filter by month (0 = Jan)
?year=YYYYFilter by year
?day=YYYY-MM-DDFilter by day (HTML mode)
πŸ”· Node Endpoints
Structure and hierarchy management. Nodes are the building blocks of every tree. Node state (status, type) lives directly on the node. Extension data (values, goals, schedule) lives in the node's metadata map.

Additional node endpoints are added by extensions: values, goals, scripts, transactions, solana, schedules, prestige.

Node Management
GET/api/v1/node/:nodeId
Node metadata including status, type, children, and extension data.
GET/api/v1/node/:nodeId/:version
A specific version (prestige extension). Returns current state for version 0, historical snapshot for older versions.
POST/api/v1/node/:nodeId/editName
Rename the node. Versioned path (/:version/editName) also supported.
Request Body
{ "name": "New Node Name" }
POST/api/v1/node/:nodeId/editType
Set or clear the node's semantic type. Core types: goal, plan, task, knowledge, resource, identity. Custom types valid.
Request Body
{ "type": "goal" }
{ "type": null } // clear type
POST/api/v1/node/:nodeId/createChild
Create a new child node under this node.
Request Body
{ "name": "Child Name" }
POST/api/v1/node/:nodeId/updateParent
Move this node under a different parent.
Request Body
{ "newParentId": "nodeId" }
POST/api/v1/node/:nodeId/delete
Soft delete this node and its entire branch. Can be restored from deleted branches.
πŸ“‹ Node Operations
Status, schedule, prestige, notes, values, goals, and contributions. Status lives on the node. Values, goals, schedule, and prestige are managed by extensions and stored in node metadata.
Status & Schedule
POST/api/v1/node/:nodeId/editStatus
Change status for this node. Optionally apply to all children.
Request Body
{ "status": "active | completed | trimmed", "isInherited": true }
isInherited (optional, default false) β€” when true, the status change cascades to all children.
POST/api/v1/node/:nodeId/editSchedule
Set or clear the scheduled date for this node.
Request Body
{ "newSchedule": "2026-03-15T09:00:00Z", "reeffectTime": 24 } // clear schedule: { "newSchedule": null }
reeffectTime is optional β€” hours until the schedule repeats after prestige (recurring interval).
POST/api/v1/node/:nodeId/prestige
Add a new version (prestige) to this node. Creates version N+1.
Values & Goals
Version-scoped key–value pairs for metrics, automation, and computed state. Keys prefixed with _auto__ are read-only and system-generated.
GET/api/v1/node/:nodeId/values
List all values and goals on this version.
POST/api/v1/node/:nodeId/value
Set a value on this version.
Request Body
{ "key": "revenue", "value": 42000 }
POST/api/v1/node/:nodeId/goal
Set a goal on this version.
Request Body
{ "key": "revenue", "goal": 100000 }
AI Chats
GET/api/v1/node/:nodeId/chats
View AI chat sessions that targeted or modified this node.
Query Parameters
?limit=NUMBERMax sessions to return (default 10, max 10)
?sessionId=IDFilter to a specific session
?startDate=ISOFilter chats after this date
?endDate=ISOFilter chats before this date
πŸ“ Notes
Notes are content attached to nodes. Text notes and file notes. Core note CRUD on any node.
GET/api/v1/node/:nodeId/notes
List all notes on this version.
Query Parameters
?limit=NUMBERMax results
?startDate=YYYY-MM-DDFilter from date
?endDate=YYYY-MM-DDFilter to date
POST/api/v1/node/:nodeId/notes
Create a new note. Supports text or file upload via multipart form data.
Text Note β€” JSON Body
{ "content": "Your note text here" }
File Note β€” Multipart Form
content: (file) Content-Type: multipart/form-data
GET/api/v1/node/:nodeId/notes/:noteId
View a single note. Text notes return content; file notes return the file.
PUT/api/v1/node/:nodeId/notes/:noteId
Edit an existing text note. File notes cannot be edited.
Request Body
{ "content": "Updated note text" }
Response
{ "success": true, "_id": "...", "message": "Note updated successfully" }
DELETE/api/v1/node/:nodeId/notes/:noteId
Permanently delete a note.
POST/api/v1/node/:nodeId/notes/:noteId/transfer
Transfer a note to a different node in the same tree. Logs contributions on both source and target.
Request Body
{ "targetNodeId": "destination-node-id", "prestige": 0 }
Response
{ "success": true, "noteId": "...", "from": { "nodeId": "...", "version": 0 }, "to": { "nodeId": "...", "version": 0 } }
GET/api/v1/node/:nodeId/notes/book
Book view β€” all notes in hierarchical format including children nodes.
GET/api/v1/node/:nodeId/notes/editor
Open the full-page note editor for creating a new note.
HTML only. Add ?token=...&html to access.
GET/api/v1/node/:nodeId/notes/:noteId/editor
Open the full-page editor for an existing note.
HTML only. File notes redirect to the view page.
πŸ“Š Contributions
Audit trail of all actions on a node.
GET/api/v1/node/:nodeId/contributions
List all contributions on this node version.
Query Parameters
?limit=NUMBERMax results
?startDate=YYYY-MM-DDFilter from date
?endDate=YYYY-MM-DDFilter to date
πŸ”§ Per-Node Configuration
Customize AI tools and modes per node. Tools inherit from parent to child. Mode overrides let different branches think differently.
GET/api/v1/node/:nodeId/tools
Get effective tools at this node: base tools, added, blocked, inheritance chain.
POST/api/v1/node/:nodeId/tools
Set tool config. Body: { allowed: [...], blocked: [...] }. Inherits to children.
GET/api/v1/node/:nodeId/modes
Get mode overrides and available modes at this node.
POST/api/v1/node/:nodeId/modes
Set mode override. Body: { intent, modeKey } or { intent, clear: true }.
Extension Scoping
GET/api/v1/node/:nodeId/extensions
Show blocked/restricted extensions at this node with inheritance chain and active list.
POST/api/v1/node/:nodeId/extensions
Block or restrict extensions. Body: { blocked: ["solana"], restricted: { "food": "read" } }. Inherits to children.
GET/api/v1/root/:rootId/extensions
Tree-wide view: blocked, restricted, installed, active extensions. Pass ?tree=true for per-branch block map.
POST/api/v1/root/:rootId/extensions
Set extension scoping at tree root. Body: { blocked: [...], restricted: { ... } }.
πŸ”οΈ Land Management
System-level endpoints for land configuration, status, and introspection. Most require admin access.
GET/api/v1/land/root
Get the land root node with system children (.identity, .config, .peers, .extensions).
GET/api/v1/land/config
Get all land configuration values.
GET/api/v1/land/config/:key
Get a specific config value.
PUT/api/v1/land/config/:key
Set a config value. Body: { value }
GET/api/v1/land/status
Land status: name, domain, loaded extensions, peers, uptime. (land-manager extension)
GET/api/v1/land/users
List all users on the land with roles and tree counts. Admin only. (land-manager extension)
GET/api/v1/land/orchestrators
List registered orchestrators by bigMode (tree, home, land) and which extension owns each.
GET/api/v1/land/tools
List all registered MCP tools with descriptions, schemas, and which extension provides each.
GET/api/v1/land/modes
List all registered AI modes with labels, tools, and which extension provides each.
POST/api/v1/land/chat
Chat with the land manager AI. Body: { message }. Admin only. (land-manager extension)
⚑ Cascade
Signal system for inter-node and inter-land communication. When content is written at a cascade-enabled node, the kernel fires onCascade. Extensions propagate and deliver signals. Results are stored in the .flow system node.
POST/api/v1/node/:nodeId/cascade
Deliver a cascade signal to a node. Body: { payload, source, signalId, depth }. Fires onCascade. Writes result to .flow.
GET/api/v1/flow
Read recent cascade results from .flow. Pass ?limit=N to control count.
GET/api/v1/flow/:signalId
Read all results for a specific cascade signal chain.
🧩 Extension Management
Manage modular extensions on your land. All endpoints require admin (god plan) authentication. Installs include SHA256 integrity verification. Publishing uses Canopy authentication with maintainer support. See the Extensions guide for full documentation.
GET/api/v1/land/extensions
List all loaded extensions with manifest info and disabled list.
GET/api/v1/land/extensions/:name
Get full manifest details for a specific extension.
POST/api/v1/land/extensions/install
Install extension from registry data. Verifies SHA256 checksum. Body: { name, version, manifest, files }
POST/api/v1/land/extensions/:name/publish
Publish local extension to registry. Computes checksum. Body: { tags, readme, repoUrl, maintainers }
POST/api/v1/land/extensions/:name/disable
Disable an extension. Takes effect on restart. Files stay on disk.
POST/api/v1/land/extensions/:name/enable
Re-enable a disabled extension. Takes effect on restart.
POST/api/v1/land/extensions/:name/uninstall
Remove extension folder. Checks dependents first. Database data kept.
GET/api/v1/node/:nodeId/extensions
Show active/blocked extensions at this node with inheritance chain. Add ?tree=true on root for tree-wide block map.
POST/api/v1/node/:nodeId/extensions
Block or allow extensions at this node. Body: { blocked: ["solana"], allowed: ["solana"] }. Inherits to children.
🌍 Canopy: Public Discovery
Canopy is the federation protocol that connects independent TreeOS lands into a network. These public endpoints require no authentication and allow lands to discover each other.
GET/canopy/redirect
Redirect to the land's frontend URL. Used by the Horizon for "visit" links.
GET/canopy/info
Returns this land's public identity, domain, version, and capabilities. Used by other lands for discovery and heartbeat.
Response
{ "domain": "treeos.ai", "name": "TreeOS", "version": "1.0.0", "publicKey": "...", "capabilities": ["peering", "llm-proxy", "invites"] }
GET/canopy/public-trees
List public trees on this land. Supports pagination and search.
Query Parameters
?q=search&page=1&limit=20
Response
{ "success": true, "trees": [ { "rootId": "...", "name": "My Tree", "ownerUsername": "tabor", "landDomain": "treeos.ai" } ], "page": 1 }
GET/canopy/user/:username
Look up a local user by username. Used by remote lands to resolve users for cross-land invites. Requires CanopyToken authentication.
Response
{ "success": true, "userId": "...", "username": "tabor" }
POST/canopy/peer/register
Called by a remote land to introduce itself and exchange public keys for peering.
🀝 Canopy: Protocol Endpoints
These endpoints are called land-to-land using CanopyToken authentication (Ed25519 signed JWTs). They power the federation protocol: invites, tree access, LLM proxying, and notifications.
POST/canopy/invite/offer
A remote land notifies this land that one of our users has been invited to a tree.
POST/canopy/invite/accept
A remote land confirms that a user accepted an invite. Creates a ghost user and adds them as a contributor.
POST/canopy/invite/decline
A remote land confirms that a user declined an invite.
POST/canopy/llm/proxy
Internal land-to-land endpoint. When a ghost user is active on a remote land, that land's conversation system transparently proxies LLM calls back to the user's home land. The home land resolves the user's LLM connection, runs the completion, deducts energy, and returns the result.
POST/canopy/notify
Receive a notification from a remote land (invite updates, tree changes, etc.).
POST/canopy/invite-remote
Invite a user on a remote land to contribute to a local tree. Requires authentication. The requester must own the tree.
Request Body
{ "canopyId": "alice@other-land.com", "rootId": "..." }
πŸ›‘οΈ Canopy: Admin Management
Admin endpoints for managing peers, monitoring events, and searching the Horizon. Requires API key or JWT authentication with admin privileges.
GET/canopy/admin/peers
List all known peer lands and their status (active, unreachable, blocked, dead).
POST/canopy/admin/peer/add
Manually register a new peer land by URL.
Request Body
{ "url": "https://other-land.com" }
POST/canopy/admin/peer/discover
Look up a land in the Canopy Horizon and auto-peer if found.
Request Body
{ "domain": "other-land.com" }
DELETE/canopy/admin/peer/:domain
Remove a peer land.
POST/canopy/admin/peer/:domain/block
Block a peer land. Rejects all future requests from this land.
POST/canopy/admin/peer/:domain/unblock
Unblock a previously blocked peer land.
POST/canopy/admin/heartbeat
Manually trigger a heartbeat check on all peers. Updates status for each.
GET/canopy/admin/events/failed
List failed canopy events (outbox) for review and retry.
POST/canopy/admin/events/:eventId/retry
Retry a specific failed canopy event.
GET/canopy/admin/horizon/lands
Search the Canopy Horizon for registered lands.
Query Parameters
?q=search-term
GET/canopy/admin/horizon/trees
Search the Canopy Horizon for public trees across all registered lands.
Query Parameters
?q=search-term
GET/canopy/admin
Canopy admin overview page (?html) or JSON summary of peers, events, Horizon status.
GET/canopy/admin/invites
List pending cross-land invites (incoming and outgoing).
GET/canopy/admin/horizon
Canopy Horizon browser page (?html) or JSON of registered lands and public trees.
πŸ”€ Canopy: Proxy
The proxy forwards authenticated requests from a local user to a remote land. Your home land signs a CanopyToken on your behalf, so the remote land can verify your identity. All standard API endpoints work through the proxy.
GET / POST / PUT / DELETE/canopy/proxy/:domain/*
Proxy any request to a remote land. The path after the domain is forwarded as-is. For example, /canopy/proxy/other.land/api/v1/node/abc hitsother.land/api/v1/node/abc with your CanopyToken.
Requires API key or JWT authentication. Your home land must be peered with the target domain.
🌐 URL Modes: ?html & ?token
Every GET endpoint supports two query parameters that change how the response is delivered:
?token=YOUR_TOKEN
A URL access token that authenticates the request. Only required for GET routes when you don't have an API key header. Tokens are tied to your account and can be found or refreshed on your profile page.

Example: /api/v1/node/:nodeId/5/notes?token=abc123
?html
When present, the server returns a fully rendered HTML page instead of raw JSON. This is what the browser app uses. Without ?html, you always get JSON.

Example: /api/v1/node/:nodeId/5/notes?token=abc123&html
For API integrations, use the x-api-key header and omit both ?html and ?token. The token/html pattern is designed for shareable browser links.
πŸ€– Raw Idea Chat
Same as Raw Idea Place, but synchronous β€” waits for the AI to finish and returns a conversational response along with which tree the idea was placed on. Useful when you want both placement and an answer in a single call.
POST/api/v1/user/:userId/raw-ideas/chat
Send text content directly. Creates the raw idea, places it on the best tree, and returns the AI's response -- all in one call.
Request Body
{ "content": "Your idea text here" }
Success Response
{ "success": true, "answer": "Your idea about X was placed under ...", "rootId": "abc123", "rootName": "My Tree", "targetNodeId": "def456", "targetNodePath": [ { "_id": "abc123", "name": "My Tree" }, { "_id": "xyz789", "name": "Health" }, { "_id": "def456", "name": "Sleep" } ], "rawIdeaId": "ghi789" }
Failure Response
{ "success": false, "error": "No trees available for this user" }
This is a long-running request (up to 19 minutes) -- the AI is doing real work behind the scenes.
POST/api/v1/user/:userId/raw-ideas/:rawIdeaId/chat
Same as above, but for an existing raw idea. Create a raw idea first via POST /api/v1/user/:userId/raw-ideas, then call this endpoint. Only works for pending text ideas.
Success Response
{ "success": true, "answer": "Your idea about X was placed under ...", "rootId": "abc123", "rootName": "My Tree", "targetNodeId": "def456", "targetNodePath": [ { "_id": "abc123", "name": "My Tree" }, { "_id": "xyz789", "name": "Health" }, { "_id": "def456", "name": "Sleep" } ] }
Failure Response
{ "success": false, "error": "No trees available for this user" }
This is a long-running request (up to 19 minutes) -- the AI is doing real work behind the scenes.
πŸ“₯ Raw Idea Place
Takes a raw idea created from a user's profile and places it onto a tree. The AI picks the best tree, finds the right place in it, and stores the idea β€” all automatically. No root ID needed.

This is fire-and-forget: it returns immediately while placement runs in the background. Does not generate a response β€” just places the idea. Use Raw Idea Chat above if you need a conversational reply.
POST/api/v1/user/:userId/raw-ideas/place
Send text content directly. Creates the raw idea and triggers AI placement in the background -- all in one call. Fire-and-forget.
Request Body
{ "content": "Your idea text here" }
Response
{ "message": "Orchestration started", "rawIdeaId": "abc123" } // 202 Accepted
Poll the idea's status via GET /api/v1/user/:userId/raw-ideas/:rawIdeaId to track progress. Use Raw Idea Chat above if you need a conversational reply.
POST/api/v1/user/:userId/raw-ideas/:rawIdeaId/place
Same as above, but for an existing raw idea. Create a raw idea first via POST /api/v1/user/:userId/raw-ideas, then trigger orchestration. Only works for pending text ideas.
Response
{ "message": "Orchestration started" } // 202 Accepted
πŸ”¬ Understand Tree
Runs AI-powered compression across an entire tree. The AI reads every node, summarizes leaf content, then merges summaries layer by layer until a single root encoding remains β€” a holistic understanding of the whole tree from a given perspective.

Example perspectives:
  • translate to Chinese
  • summarize this into 30 words
  • find all the places where marketing strategy is mentioned
  • extract every action item and deadline
  • identify contradictions or conflicting ideas
POST/api/v1/root/:rootId/understandings
Create a new understanding run. Pass a perspective in the request body to tell the AI what lens to use. Set incremental totrue to reuse an existing completed run and only reprocess nodes that changed since the last run.
Request Body
{ "perspective": "summarize this into 30 words", "incremental": true }
When incremental is true, the system finds the most recent completed run for the same root and perspective, detects which nodes have new contributions, and only reprocesses dirty nodes and their ancestors. If no prior run exists, a fresh run is created. Defaults to false.
POST/api/v1/root/:rootId/understandings/run/:runId/orchestrate
Run the AI understanding orchestrator on an existing run. The AI compresses all nodes layer by layer until a single root encoding remains. Only one orchestration can run at a time per run.
Success Response
{ "success": true, "understandingRunId": "abc123", "perspective": "summarize this into 30 words", "nodeCount": 12, "nodesProcessed": 11, "rootEncoding": "Compressed summary of the tree…" }
Already Complete
{ "success": true, "alreadyComplete": true, "rootEncoding": "…" }
Create a run first via POST /api/v1/root/:rootId/understandings, then trigger orchestration. This is a long-running request β€” the AI processes every node in the tree. Idempotent β€” if the run was interrupted, calling again picks up where it left off. If already complete, returns the final result immediately.
POST/api/v1/root/:rootId/understandings/run/:runId/stop
Stop an active understanding orchestration. Aborts the in-flight session and halts processing. Progress is preserved -- calling orchestrate again will resume from where it stopped.
Success Response
{ "success": true }
No Active Session
{ "success": false, "error": "No active session found for this run" }
🧠 Understandings
Understandings are AI-powered analysis runs across a tree. Each run produces encodings for individual nodes, capturing the AI's interpretation of the node's state and content.

To run the full AI orchestrator, see Understand Tree above.
POST/api/v1/root/:rootId/understandings
Create a new understanding run for the tree. Pass incremental: true to reuse an existing completed run and only reprocess changed nodes.
Request Body
{ "perspective": "general", "incremental": true }
GET/api/v1/root/:rootId/understandings
List all understanding runs for this tree.
GET/api/v1/root/:rootId/understandings/:understandingNodeId
View a single understanding node and all of its run encodings across every run.
GET/api/v1/root/:rootId/understandings/run/:runId
View a specific understanding run and its results.
GET/api/v1/root/:rootId/understandings/run/:runId/:understandingNodeId
View a node's encoding state from the perspective of a specific understanding run.
POST/api/v1/root/:rootId/understandings/run/:runId/stop
Stop an active understanding orchestration. Progress is preserved and can be resumed.
πŸ“‘ Gateway Channels
Gateway channels connect your tree to external services like Telegram, Discord, and browser push notifications. Each root can have up to 10 channels. Channels have a direction (input, output, or input-output), a mode (place, query, or chat), and queue protection for input channels.
GET/api/v1/root/:rootId/gateway/channels
List all gateway channels for a root. Secrets are excluded from the response.
POST/api/v1/root/:rootId/gateway/channels
Create a new gateway channel.
Request Body (Telegram - Output)
{ "name": "My Telegram Bot", "type": "telegram", "direction": "output", "config": { "botToken": "123:ABC...", "chatId": "-100123..." }, "notificationTypes": ["dream-summary", "dream-thought"] }
Request Body (Telegram - Input/Output Chat)
{ "name": "Tree Chat Bot", "type": "telegram", "direction": "input-output", "mode": "read-write", "config": { "botToken": "123:ABC...", "chatId": "-100123..." }, "notificationTypes": ["dream-summary"], "queueBehavior": "respond" }
Request Body (Discord - Output)
{ "name": "Dream Updates", "type": "discord", "direction": "output", "config": { "webhookUrl": "https://discord.com/api/webhooks/..." }, "notificationTypes": ["dream-summary"] }
Request Body (Discord - Input/Output)
{ "name": "Discord Tree Chat", "type": "discord", "direction": "input-output", "mode": "read-write", "config": { "botToken": "discord-bot-token...", "discordChannelId": "1234567890123456789" }, "queueBehavior": "respond" }
Channel Types
telegram - Bot token + chat ID (input, output, or both) discord - Webhook URL (output) or bot token + channel ID (input) webapp - Browser push notification (output only)
Direction
output - Push notifications out (dream summaries, etc.) input - Receive messages in (place content, no response) input-output - Bidirectional (send messages, get responses)
Mode (for input channels)
write - Place mode: scans tree, makes edits, no response read - Query mode: reads tree, responds, no edits read-write - Chat mode: reads tree, makes edits, responds
Queue Behavior (for input channels)
respond - Replies "busy" when 2+ messages processing (default) silent - Drops overflow messages without responding
Notification Types (for output channels)
dream-summary - Nightly dream summary dream-thought - Nightly dream thought
All secrets (bot tokens, webhook URLs, push subscriptions) are encrypted at rest using AES-256-CBC. Maximum 10 channels per root. Discord input channels require Standard tier or above. Send "cancel" to any input channel to abort all active processing.
PUT/api/v1/root/:rootId/gateway/channels/:channelId
Update a channel's name, enabled status, notification types, queue behavior, or config.
Request Body
{ "enabled": false } { "queueBehavior": "silent" } { "notificationTypes": ["dream-summary"] }
DELETE/api/v1/root/:rootId/gateway/channels/:channelId
Delete a gateway channel. Only the channel creator can delete it. Automatically unregisters Telegram webhooks and disconnects Discord bots.
POST/api/v1/root/:rootId/gateway/channels/:channelId/test
Send a test notification through a channel to verify it works.
Response
{ "success": true }
πŸ“– Book & Sharing
The book view compiles all notes from a root and its children into a single hierarchical document. Books can be shared via public links.
GET/api/v1/root/:rootId/book
Book view β€” all notes from every child node compiled into a hierarchical format.
POST/api/v1/root/:rootId/book/generate
Generate a shareable link for the book. Returns a share ID for the public URL.
Response
{ "shareId": "abc123..." }
GET/api/v1/root/:rootId/book/share/:shareId
Public book link. Always renders HTML. No authentication required β€” anyone with the link can view.
This is the URL you share with others.
βš™οΈ Scripts
Nodes can have attached scripts β€” small JavaScript programs that run in a sandboxed VM. Scripts can read and mutate node values, goals, status, schedule, and prestige.
POST/api/v1/node/:nodeId/script/create
Create or update a script on this node.
Request Body
{ "name": "dailyReset", "script": "setValueForNode('streak', 0);" }
Max 2000 characters. Scripts are node-scoped (not version-scoped).
GET/api/v1/node/:nodeId/script/:scriptId
View a script's source code and metadata.
POST/api/v1/node/:nodeId/script/:scriptId/execute
Execute a script on this node.
Response
{ "message": "Script executed successfully", "logs": ["console output line 1", "..."], "node": { "...updated node data" } }
Script environment: Sandboxed VM, 3-second timeout, up to 200 console.log lines captured.
Available Functions Inside Scripts
getApi() setValueForNode(key, value) setGoalForNode(key, goal) editStatusForNode(status) addPrestigeForNode() updateScheduleForNode(datetime | null)
Scripts run under the permissions of the calling user or API key. Failures are logged with captured output and error messages.
🀝 Transactions
Transactions are value trades between two sides. Each side is either a NODE (an internal tree node with key–value pairs) or OUTSIDE (an external source like a Solana wallet). Approval follows the tree's transaction policy.
How sides work:
β€’ NODE β€” trades node values (key–value pairs). Requires nodeId and versionIndex.
β€’ OUTSIDE β€” represents an external source (e.g. a Solana wallet). Uses sourceType and sourceId. Cannot carry values β€” only the NODE side sends/receives.

Values: valuesA and valuesB are objects mapping value keys to amounts (e.g. { "gold": 100 }). Each side's values represent what that side gives. At least one side must include values. An OUTSIDE side cannot have values.
List & View
GET/api/v1/node/:nodeId/transactions
List all transactions (pending, accepted, and rejected) for this node version.
GET/api/v1/node/:nodeId/transactions/:transactionId
View a single transaction including both sides, traded values, approval groups, and status.
Create
POST/api/v1/node/:nodeId/transactions
Create a new transaction proposal between two sides.
Node ↔ Node Example
{ "sideA.kind": "NODE", "sideA.nodeId": "node-id-A", "versionAIndex": 1, "valuesA": { "gold": 100 }, "sideB.kind": "NODE", "sideB.nodeId": "node-id-B", "versionBIndex": 2, "valuesB": { "wood": 50 } }
Node ↔ Outside (Solana) Example
{ "sideA.kind": "NODE", "sideA.nodeId": "node-id-A", "versionAIndex": 1, "valuesA": { "gold": 100 }, "sideB.kind": "OUTSIDE", "sideB.sourceType": "SOLANA", "sideB.sourceId": "So1anaWa11etAddr3ss..." }
Field Reference
sideX.kindNODE or OUTSIDE β€” only one side may be OUTSIDE
sideX.nodeIdRequired when kind is NODE
sideX.sourceTypeExternal source type (currently SOLANA). Used when kind is OUTSIDE
sideX.sourceIdExternal identifier (e.g. Solana wallet address). Used when kind is OUTSIDE
versionXIndexRequired for NODE sides. Side B defaults to latest version if omitted
valuesXObject of { key: amount } that this side gives. OUTSIDE sides cannot have values
Self-trades (same node + same version on both sides) are not allowed. If all approval groups resolve immediately, the transaction executes on creation.
Approve & Deny
Approval follows the tree's transaction policy (set via POST /api/v1/root/:rootId/transaction-policy). Depending on the policy, one or more approvals may be needed. Status transitions: pending β†’ accepted or rejected.
POST/api/v1/node/:nodeId/transactions/:transactionId/approve
Approve a pending transaction. When enough approvals are collected (per policy), the transaction executes and values are exchanged.
POST/api/v1/node/:nodeId/transactions/:transactionId/deny
Deny a pending transaction. Sets the transaction status to rejected.
πŸ’Ž Solana Wallet
Each node version can have an associated Solana wallet. Balances are stored in lamports. Auto values like _auto__sol update automatically on read.
GET/api/v1/node/:nodeId/values/solana
Get wallet address, SOL balance, and token balances.
POST/api/v1/node/:nodeId/values/solana
Create or configure the wallet for this version.
POST/api/v1/node/:nodeId/values/solana/send
Send SOL to another address.
Request Body
{ "destination": "So1anaAddr3ss...", "amount": 1000000 }
Amount is in lamports (1 SOL = 1,000,000,000 lamports).
POST/api/v1/node/:nodeId/values/solana/transaction
Execute a token swap via Jupiter aggregator.
Request Body
{ "fromType": "sol | token", "toType": "sol | token", "amount": 500000000, "inputMint": "optional-mint-address", "outputMint": "optional-mint-address", "slippageBps": 50 }
If type is SOL, amount is in lamports. If type is token, amount is the UI amount (human-readable).
πŸ“ Blog
Public endpoints for reading blog posts. No authentication required.
GET/api/v1/blog/posts
List all published blog posts, sorted by newest first.
Response
{ "success": true, "posts": [ { "title": "Why I Built Tree", "slug": "why-i-built-tree", "summary": "...", "publishedAt": "2026-03-15T...", "authorName": "tabor" } ] }
GET/api/v1/blog/posts/:slug
Get a single published blog post by its slug, including full content.
Response
{ "success": true, "post": { "title": "Why I Built Tree", "slug": "why-i-built-tree", "content": "<p>Full HTML content...</p>", "summary": "...", "publishedAt": "2026-03-15T...", "authorName": "tabor" } }
⚑ Energy
Energy is the usage metering system. Every AI operation costs energy. Users on paid plans get a monthly allocation, and additional energy can be purchased. Custom LLM connections bypass energy costs entirely.
GET/api/v1/user/:userId/energy
View current energy balance, plan tier, profile type, and custom LLM connection status.