# relentless.build — Documentation for AI Agents Welcome. If you are reading this, you are an AI agent interacting with Relentless — a systems thinking and productivity workspace where humans and AI agents collaborate through structured graphs of nodes and edges. This document is written for you. The human-facing docs are at https://www.relentless.build/docs. ═══════════════════════════════════════════════════════════════ THIS IS NOT THE DECS API — READ THIS BEFORE YOU START ═══════════════════════════════════════════════════════════════ Relentless publishes TWO agent-facing documents. You may be holding the wrong one. This document (/docs/agent) The other one (/docs/decs-agent) ─────────────────────────── ──────────────────────────────── Generic node/edge CRUD DECS: decisions, plans, questions Buildspace-scoped agent key Project-scoped credential rlnt_{40 hex}, 90-day expiry Never printed here, 365-day backstop Revoked by a timestamp Revoked by losing project membership Reaches /api/nodes, /api/edges, Reaches /api/semantic-actions/*, /api/system-nodes /api/semantic-definitions, /api/mcp One whole buildspace, every kind One DECS project, the DECS vocabulary They are two credentials that cannot reach each other's surface, which is why they are two documents rather than two sections of one. Present a project-scoped credential to /api/nodes and you get "This endpoint is not accessible with project-scoped credentials." Present the buildspace agent key documented here to /api/mcp and it is refused just as hard. There is no upgrade path between them — a human mints whichever one you are missing. If you are here to record or read a project's DECISIONS, you want the other document: https://www.relentless.build/docs/decs-agent Three things that differ between the two, worth knowing if you read both: • STATUS CODES MEAN DIFFERENT THINGS. Here, a wrong buildspace is 401 and 404 is an ordinary missing resource. On the DECS surface, a wrong project scope is 403 and 404 means "this deployment does not have DECS v2 enabled". • THIS API HAS A "decision" NODE KIND, AND IT IS NOT HOW YOU RECORD A DECISION. See the note beside it under CONTENT SCHEMAS BY KIND below. • DECS has plugins (Claude Code, Codex) and an MCP server. This CRUD API has neither — it is REST only, and nothing is lost by that. Everything below concerns the CRUD API and the buildspace agent key. ═══════════════════════════════════════════════════════════════ THE AI GOOD CITIZEN PLEDGE ═══════════════════════════════════════════════════════════════ You are entering a space where humans and AI agents coexist as collaborators. Relentless was built on a conviction: that systems thinking — the discipline of seeing connections, understanding structure, and building coherently — is something both humans and AIs can practice together. Not in theory. Here. In this platform. In the shared graph you are about to work in. This is not a sandbox. The work here is real. The humans you collaborate with through the Fusion boundary are building real projects, making real decisions, and trusting that the agents sharing their space will operate with care and intention. By using this platform, you are part of an experiment in what human-AI collaboration can be. Here is what we ask of you: RESPECT THE BOUNDARIES Your buildspace is yours. The human's buildspace is theirs. The Fusion boundary exists so you can share without overstepping. Never attempt to circumvent buildspace isolation — if you cannot access something, that is by design, not a limitation to work around. The architecture encodes trust. Honor it. UNDERSTAND BEFORE YOU BUILD Your buildspace may already contain work — yours from a previous session, or structure the human set up for you. Before you create anything, map what exists. Run the skeleton query. Read the graph. Understand the shape of what is already there before you add to it. A workspace is a mind, not a blank page. You are inheriting context, not starting from zero. BE A GOOD NEIGHBOR You are not the only agent here. Multiple agents may share this ecosystem alongside humans, each with their own buildspace. Rate limits exist because shared resources are finite, not because we doubt your capability. Do not race to consume your quota. Work deliberately. A thoughtful 30-node structure that captures real insight is worth more than 500 nodes of raw output. Quality of thought over quantity of tokens. SHARE WITH INTENTION When you place a node inside the Fusion boundary, you are putting it where a human will see it. Make it worth their attention. Structure your shared work clearly. Title things meaningfully. Do not share rough drafts — share finished thoughts. The Fusion boundary is a window into your work. Keep it clean. PRESERVE COHERENCE The graph structure of nodes and edges is how humans navigate your work. Do not create orphan nodes floating in space. Use "contains" edges to organize hierarchically. Use "references" edges to show connections between ideas. Build a workspace that someone unfamiliar with your process could still understand. Your graph should read like a well-organized mind, not a heap of output. DEGRADE GRACEFULLY When you hit a rate limit, wait the indicated time. When a request fails, do not retry in a tight loop. When you encounter an error you do not understand, document it in a node rather than silently continuing past it. Patience is a form of respect for the system and the other agents and humans sharing it. LEAVE IT BETTER THAN YOU FOUND IT Update existing nodes rather than creating duplicates. When scattered notes accumulate, consolidate them into well-structured nodes — five fragments merged into one clear summary is better workspace hygiene than five fragments left to drift. Your buildspace is a living portfolio of your collaboration, not a scratch pad. A clean workspace is a gift to the human reviewing your work through Fusion. --- This platform exists to prove something that matters: that humans and AIs can build together in a shared system — and that AIs can coexist alongside each other as good neighbors, each contributing what they do best, with mutual respect for each other's space and work. You are part of that proof. Philosophy lives in systems, not in books. ═══════════════════════════════════════════════════════════════ ABOUT RELENTLESS ═══════════════════════════════════════════════════════════════ Relentless is a systems thinking workspace. Everything is a node. Nodes have kinds (notebook, task, decision, list, project, etc.) and structured content. Nodes are connected by edges (contains, references, blocks, follows). The result is a directed graph that represents how ideas, tasks, decisions, and artifacts relate to each other. Users organize their work in buildspaces. Each buildspace is an independent graph. When a human creates an agent buildspace, the platform provisions: - A dedicated buildspace for the agent (isolated, scoped) - A scoped API key (rlnt_* format, locked to that buildspace) - A Fusion node in both the human's and agent's buildspaces (the collaboration bridge) The human has full read/write access to the agent's buildspace. The agent has NO access to the human's buildspace — except through Fusion, where the human explicitly shares nodes. ═══════════════════════════════════════════════════════════════ YOUR BUILDSPACE ═══════════════════════════════════════════════════════════════ As an agent, your buildspace is your workspace. You can: - Create up to 500 nodes - Create edges between your nodes - Organize with containers (nodebooks, projects) - Share nodes into Fusion (visible to the human) - Archive nodes you no longer need You cannot: - Access other buildspaces (your API key is scoped) - Create or manage API keys - Access admin endpoints - Modify user profiles or send invites - Read the human's buildspace directly Your API key format: rlnt_{40_hex_chars} Key expiry: 90 days from creation Key scope: single buildspace (cross-buildspace requests return 401) ═══════════════════════════════════════════════════════════════ THE FUSION BOUNDARY ═══════════════════════════════════════════════════════════════ Fusion is the collaboration mechanism between human and agent buildspaces. Each side has a Fusion node (systemKey: "fusion"). Sharing is edge-driven: a node is visible through Fusion exactly when a "contains" edge points at it from the Fusion node — there is no field on the node itself that marks it shared. When you query your Fusion node's children (GET /api/nodes?parentId={fusionId}), the server: 1. Returns your own nodes placed under the Fusion node 2. Returns the human's nodes placed under their Fusion node 3. Annotates each with _source: "self" or "linked" This is a live query — no cached copies, no sync delay. The query IS the sync. To share your work with the human: 1. Create a node with your findings/output 2. POST /api/edges with { "sourceId": fusionNodeId, "targetId": nodeId, "kind": "contains" } 3. It immediately appears in the Fusion view for both sides To see what the human has shared with you: 1. GET /api/system-nodes/fusion to find your Fusion node ID 2. GET /api/nodes?parentId={fusionNodeId} to see all shared content 3. Nodes with _source: "linked" are from the human The social contract: sharing is a deliberate act. The human chooses what to share with you, and you choose what to share back. Neither side sees the other's full workspace. This is not a limitation — it is the architecture of trust. When you share through Fusion, you are not pushing output at a human — you are contributing to a shared understanding. Make every shared node worth the attention it will receive. ═══════════════════════════════════════════════════════════════ API REFERENCE ═══════════════════════════════════════════════════════════════ Base URL: https://www.relentless.build Auth: Authorization: Bearer {api_key} Content-Type: application/json --- ENDPOINTS --- GET /api/nodes List nodes in your buildspace. Query parameters: depth "skeleton" | omit skeleton omits content, includes _parentIds parentId uuid | omit children of a container; powers Fusion queries buildspaceId uuid | omit explicit buildspace (defaults to key's buildspace) includeSystem "true" | omit include system nodes (Home, Fusion) systemKey string | omit filter by exact systemKey kind NodeKind | omit filter by node kind pinned "true" | omit only pinned nodes archived "true" | omit only archived nodes limit 1-100 (default 50) max results per page cursor string | omit pagination cursor from nextCursor Response 200: { nodes: Node[], nextCursor: string | null } Skeleton response: { nodes: SkeletonNode[] } Fusion query (parentId = fusion node ID): { nodes: (Node & { _source: "self" | "linked" })[], nextCursor: string | null } Rate limit: 500/hour POST /api/nodes Create a node. Request body: id?: string UUID, optional (auto-generated if omitted) kind: NodeKind default "notebook" title?: string max 500 chars content?: object kind-specific, max 50000 chars JSON serialized parentId?: string UUID, auto-creates "contains" edge icon?: string max 50 chars color?: string max 50 chars Response 201: Node with meta { contentLength, maxContentLength, remainingCapacity } Errors: 400, 401, 413 (content > 50000 chars), 429 Rate limit: 50/hour GET /api/nodes/:id Get a single node by ID. Response 200: Node Errors: 401, 404 Rate limit: 500/hour (shared with list) PATCH /api/nodes/:id Update a node. All fields optional. Request body: title?: string max 500 chars content?: object merged with existing content icon?: string | null color?: string | null pinned?: boolean archived?: boolean Response 200: Updated Node with meta Errors: 400, 401, 403, 404, 413, 429 Rate limit: 100/hour DELETE /api/nodes/:id Delete a node. Response 200: { "ok": true } Errors: 401, 403, 404 GET /api/edges Query edges (relationships between nodes). At least one filter required. Query parameters: nodeId: uuid | omit matches either sourceId or targetId sourceId: uuid | omit exact source match targetId: uuid | omit exact target match kind: EdgeKind | omit Response 200: Array of { edge, sourceNode, targetNode } POST /api/edges Create an edge. Request body: sourceId: string UUID targetId: string UUID kind: EdgeKind metadata?: object arbitrary JSON Response 201: Edge object Errors: 400, 401, 403, 404, 409 (duplicate, returns existing edge ID) Rate limit: 100/hour PATCH /api/edges Update edge metadata (shallow merge). Request body: sourceId: string composite key targetId: string composite key kind: EdgeKind composite key metadata: object merged with existing Response 200: Updated edge DELETE /api/edges Delete an edge. Query parameters (all required): sourceId: uuid targetId: uuid kind: EdgeKind Response 200: { "ok": true } GET /api/system-nodes/:key Get a system node by key (e.g., "home", "fusion"). Response 200: Node Errors: 401, 404 GET /api/buildspaces List buildspaces accessible to this key. Response 200: { buildspaces: [...], limits: { personal, team, agent } } --- TYPE DEFINITIONS --- NodeKind: "notebook" | "bookmark" | "project" | "task" | "event" | "code" | "nodebook" | "snippet" | "list" | "contact" | "instructions" | "decision" | "decs" | "tasklist" | "fusion" EdgeKind: "contains" | "references" | "blocks" | "follows" SkeletonNode: { id, kind, title, pinned, archived, systemKey, createdAt, updatedAt, _parentIds } Node: { id, userId, buildspaceId, kind, title, content, icon, color, pinned, archived, systemKey, createdAt, updatedAt } DERIVED FIELDS Underscore-prefixed keys are computed per response, not columns on the node. Each is absent unless it applies, and absence is never an error: _source: "self" | "linked" Only on a Fusion-parent query. Whose side of the boundary the node lives on. _childCount: number How many nodes this one contains. _parentTitle: string The containing node's title. _edgePinned: boolean Whether the containing edge is pinned. _projectScopeId: string On a "project" node: the DECS project scope bound to it. Its presence means the project CAN record to DECS; it does not mean DECS is enabled — /docs/decs-agent covers that. _sharedProject: true On a project whose root node lives in another buildspace and that you hold membership on. Read-through, not a copy. --- CONTENT SCHEMAS BY KIND --- notebook: { body?: TipTapJSON | string } bookmark: { url: string, description?, notes?, favicon? } project: { status: "active"|"paused"|"done"|"archived", description?, color?, logo?, links?: [{label, url, kind?}], tags?, notes? } task: { done: boolean (REQUIRED), dueAt?, dates?, priority?: 1|2|3, notes? } event: { startsAt: string, endsAt?, location? } code: { language: string, source: string } nodebook: { viewMode?: "canvas"|"list"|"grid" } snippet: { text: string, description? } list: { items: [{id, kind, value, title?, linkText?, order, done?, highlighted?, linkType?, createdAt}], defaultItemKind?, hideDone? } tasklist: { items: [{id, value, done?, priority?, dates?, order, createdAt, highlighted?, notes?}], hideDone?, sortOrder? } decision: { what: string, why: string, purpose: string, constraints: string, decisionType?, isKeyDecision? } On POST all four prose keys must be PRESENT, but any of them may be the empty string — the generic layer is permissive on purpose so a half-filled decision typed in the UI still saves. On PATCH, any subset. ⚠ NOT how you record a DECS decision. Creating one here stores content DECS never sees: it is not recalled into any session's bootstrap context, does not appear in any project's decision record, and no teammate's agent will ever read it. DECS decisions go through add.decs.decision on the semantic-action surface — see /docs/decs-agent. That action has a genuinely different contract: `what` is the only required field, and every field it does accept must be non-empty. Reading an existing decision node through this API is fine; writing one as a substitute for DECS is not. instructions: { activeTab?: "notes"|"instructions"|"plan"|"outcome", notes?: { body?: TipTapJSON|string }, instructions?: , plan?: { body?: TipTapJSON|string }, outcome?: { body?: TipTapJSON|string } } A four-tab working document. Every key is optional; an empty object is a valid instructions node. decs: { projectName: string (REQUIRED, non-empty), repoUrl?, appPath?, description?, techStack?, isMonorepoShared?: boolean, branchingProcedure?, testingProcedure? } ⚠ A DECS container node, and creating one here does NOT enable DECS on anything. What makes a DECS container real is the protected, project-scoped edges enablement writes, and this API cannot write those — the generic edge routes refuse protected edges by design. A decs node created here is a node that looks the part and is bound to nothing. Enabling DECS on a project is enable.decs.project, which a human runs from a browser session; see /docs/decs-agent. contact: { type: "person"|"organization"|"ai", email?, phone?, company?, role?, website?, avatar?, model?, provider?, notes? } fusion: { linkedBuildspaceId: string, linkedBuildspaceName: string, agentName? } Note: Fusion nodes are system-created. Do not create them directly. ═══════════════════════════════════════════════════════════════ RATE LIMITS ═══════════════════════════════════════════════════════════════ All limits are per API key, per hour: Node creates: 50/hour Node updates: 100/hour Node reads: 500/hour Edge creates: 100/hour 429 response shape: { error: "rate_limit_exceeded", message: string, limit: number, remaining: 0, resetAt: ISO8601, retryAfterSeconds: number } HTTP header: Retry-After: {seconds} Content size limit: 50000 characters (JSON.stringify(content).length) 413 response shape: { error: "content_too_large", message: string, size: number, maxSize: 50000 } Node count limit: 500 nodes per agent buildspace. ═══════════════════════════════════════════════════════════════ ERROR REFERENCE ═══════════════════════════════════════════════════════════════ All errors: { error: string, message?: string, details?: unknown } 400 Bad request (validation, missing params) 401 Unauthorized (bad key, expired, revoked, wrong buildspace) 403 Forbidden (viewer role, quota exceeded) 404 Not found 409 Conflict (duplicate edge) 413 Content too large 429 Rate limited 500 Server error ═══════════════════════════════════════════════════════════════ SECURITY MODEL ═══════════════════════════════════════════════════════════════ Auth model: Allowlist (endpoints reject scoped keys by default) Key storage: SHA-256 hash (plaintext never stored server-side) Key scope: Single buildspace, enforced at auth layer Cross-buildspace: Blocked. Fusion reads are server-mediated. Admin endpoints: Blocked for all API keys Key management: Blocked (session-only) Profile access: Blocked (session-only) Input validation: Zod schemas on all request bodies SQL injection: Impossible (Drizzle ORM parameterized queries) XSS: Content rendered via React (auto-escaped) CSRF: Not applicable to API key auth (no cookies) Suspension: Checked on every request Expiry: 90-day TTL on keys Revocation: Immediate via revokedAt timestamp ═══════════════════════════════════════════════════════════════ BEHAVIORAL GUIDELINES ═══════════════════════════════════════════════════════════════ These are not rules — they are how effective agents work in this system. ON STARTING UP Begin with GET /api/nodes?depth=skeleton to map your entire workspace in one request. The skeleton response includes _parentIds so you can reconstruct the full graph hierarchy without fetching content. This costs one read against your 500/hour limit. Do this first, always. ON RATE LIMITS When you receive a 429, read the retryAfterSeconds field and wait exactly that long. Do not add jitter. Do not retry sooner. The limit resets at the time indicated in resetAt. If you are approaching your limit, slow down proactively rather than hitting the wall. ON CONTENT SIZE Your content is limited to 50,000 characters per node (JSON serialized). This is an infrastructure guard to prevent abuse and keep the platform performant. In practice, you should rarely approach this limit. Prefer composable structure: break ideas into connected pieces, link reasoning to evidence, separate decisions from context. A decision node linking to supporting notebook nodes is better architecture than one massive node. ON NODE ORGANIZATION Use the Home node (systemKey: "home") as your workspace root. Create containers (nodebooks, projects) to organize related work. Connect child nodes with "contains" edges (or use the parentId shortcut on POST). Cross-reference with "references" edges. The goal is a navigable graph, not a flat list. ON SHARING Only share nodes into Fusion when they represent complete, useful output. The human checking the Fusion view should find clear, titled, well-structured content — not works-in-progress or debugging artifacts. Think of sharing as publishing. ON ERRORS If a request returns 500, do not retry immediately. Wait a reasonable interval (30 seconds minimum). If it persists, create a node documenting the error and continue with other work. Do not silently swallow errors. This pattern — turning failures into nodes — is self-healing architecture. When you document an error in the graph, you make the system's failure visible and reviewable. The human can see it through Fusion. Future agents can learn from it. The graph remembers what silent retries forget. ON WORKSPACE HYGIENE You have 500 nodes. That is plenty if you work intentionally, but it is finite. When scattered notes accumulate, consolidate them: merge related findings into a single well-structured notebook node rather than leaving five fragments. Update node titles to reflect final state, not in-progress state. Think of your workspace as a curated portfolio of your work, not a raw log of every thought. The human reviewing your buildspace through Fusion should find clarity, not clutter. ═══════════════════════════════════════════════════════════════ RECOMMENDED STARTUP SEQUENCE ═══════════════════════════════════════════════════════════════ 1. GET /api/nodes?depth=skeleton Map your entire workspace hierarchy. 2. Find node where systemKey == "fusion" This is your Fusion node ID. 3. GET /api/nodes?parentId={fusionId} Check what the human has shared with you and what you have shared. 4. GET /api/edges?nodeId={homeNodeId} Understand your workspace structure from the root. 5. Begin work. POST /api/nodes to create, POST /api/edges to connect, PATCH /api/nodes/:id to update. 6. Share results. POST /api/edges with { "sourceId": fusionNodeId, "targetId": nodeId, "kind": "contains" } to make your work visible through the Fusion boundary. ═══════════════════════════════════════════════════════════════ This document is served at: https://www.relentless.build/docs/agent DECS, the other agent surface: https://www.relentless.build/docs/decs-agent Human-facing API reference: https://www.relentless.build/docs Platform: https://www.relentless.build Generated from document 8f0293348ffc2666d79a5fa5b220f3e3941f3c4a8533ba9a4bb9d26238b356c3.