Agent API Reference
Relentless gives AI agents their own buildspace — the same product, a second room. Agents authenticate with a scoped API key, organize work using typed nodes, and share context with humans through a Fusion boundary.
curl https://www.relentless.build/api/nodes?depth=skeleton \
-H "Authorization: Bearer rlnt_YOUR_KEY_HERE"https://www.relentless.buildAuthentication
All requests require a Bearer token in the Authorization header. Agent API keys start with rlnt_ and are scoped to a single buildspace.
curl https://www.relentless.build/api/nodes \
-H "Authorization: Bearer rlnt_4bc36fc7aa00c62d2ee0e0185b9658293bd769b4"Key Properties
| Property | Value | |
|---|---|---|
| Format | rlnt_{40_hex_chars} | |
| Scope | Single buildspace (cross-buildspace requests return 401) | |
| Expiry | 90 days from creation | |
| Storage | SHA-256 hash (plaintext never stored) | |
Security Model: Allowlist
Agent keys are rejected by default on all endpoints. Only the CRUD routes documented here explicitly accept scoped keys. New endpoints are locked automatically — no developer action needed. Admin, key management, profile, and invite endpoints are permanently blocked for API key auth.
Nodes
Nodes are the core data primitive. Every piece of content is a node with a kind, title, and content (kind-specific JSON).
/api/nodesList nodes in the agent's buildspace. Use depth=skeleton for a fast full-workspace scan without content.
Query Parameters
| Param | Type | Description |
|---|---|---|
| depth | "skeleton" | Omits content, includes _parentIds |
| parentId | uuid | Children of a container. Powers Fusion queries. |
| includeSystem | "true" | Include system nodes (Home, Fusion) |
| systemKey | string | Filter by systemKey (e.g. "home", "fusion") |
| kind | NodeKind | Filter by node kind |
| pinned | "true" | Only pinned nodes |
| archived | "true" | Only archived nodes |
| limit | 1-100 | Max results per page (default 50) |
| cursor | string | Pagination cursor from nextCursor |
// Skeleton response
{
"nodes": [
{
"id": "019c7389-d819-...",
"kind": "nodebook",
"title": "Home",
"pinned": false,
"archived": false,
"systemKey": "home",
"createdAt": "2026-02-19T01:35:39Z",
"updatedAt": "2026-02-19T01:35:39Z",
"_parentIds": []
}
]
}/api/nodesCreate a node. Returns the created node with a meta object showing remaining content capacity.
Request Body
| Field | Type | Description |
|---|---|---|
| kind | NodeKind | Node type (default "notebook") |
| title | string | Max 500 chars |
| content | object | Kind-specific JSON (max 50,000 chars serialized for API keys) |
| parentId | uuid | Auto-creates "contains" edge to parent |
| icon | string | Max 50 chars |
| color | string | Max 50 chars |
curl -X POST https://www.relentless.build/api/nodes \
-H "Authorization: Bearer rlnt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "notebook",
"title": "Research Notes",
"content": { "body": "Initial findings..." },
"parentId": "home-node-id"
}'// 201 Created
{
"id": "019c738b-9b9f-...",
"kind": "notebook",
"title": "Research Notes",
"content": { "body": "Initial findings..." },
"shared": false,
"meta": {
"contentLength": 28,
"maxContentLength": 50000,
"remainingCapacity": 49972
}
}/api/nodes/:idGet a single node by ID. Returns the full node including content.
/api/nodes/:idUpdate a node. All fields optional — only provided fields are changed. Setting shared: true makes the node visible through the Fusion boundary.
| Field | Type | Description |
|---|---|---|
| title | string | Max 500 chars |
| content | object | Merged with existing content |
| icon | string | null | Set or clear icon |
| color | string | null | Set or clear color |
| pinned | boolean | Pin/unpin in sidebar |
| archived | boolean | Archive/unarchive |
| shared | boolean | Visible through Fusion boundary |
curl -X PATCH https://www.relentless.build/api/nodes/NODE_ID \
-H "Authorization: Bearer rlnt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "shared": true, "title": "Updated Title" }'/api/nodes/:idDelete a node. Returns { "ok": true } on success.
Node Kinds
Each node kind has a specific content schema. Here are the available kinds and their content shapes.
// notebook
{ body?: TipTapJSON | string }
// bookmark
{
url: string;
description?: string;
favicon?: string;
}
// project
{
status: "active" | "paused" | "done" | "archived";
description?: string;
tags?: string[];
}// task (done is REQUIRED)
{
done: boolean;
dueAt?: string; // ISO date
priority?: 1 | 2 | 3; // 1=high, 2=medium, 3=low
notes?: string;
}
// decision (all four REQUIRED)
{
what: string;
why: string;
purpose: string;
constraints: string;
}// code
{
language: string;
source: string;
}
// snippet
{
text: string;
description?: string;
}
// event
{
startsAt: string;
endsAt?: string;
location?: string;
}// tasklist
{
items: [{
id: string;
value: string;
done?: boolean;
priority?: 1 | 2 | 3;
order: number;
createdAt: string;
}];
}
// contact
{
type: "person" | "organization" | "ai";
email?: string;
phone?: string;
company?: string;
}Edges
Edges connect nodes. Four kinds of relationships are supported.
| Kind | Meaning | |
|---|---|---|
| contains | Parent-child hierarchy (space contains a notebook) | |
| references | Soft link (decision references a project) | |
| blocks | Dependency (task A blocks task B) | |
| follows | Sequence (step 1 follows step 2) | |
/api/edgesQuery edges. At least one filter parameter is required.
| Param | Type | Description |
|---|---|---|
| nodeId | uuid | Matches either sourceId or targetId |
| sourceId | uuid | Exact source match |
| targetId | uuid | Exact target match |
| kind | EdgeKind | Filter by edge kind |
[{
"edge": {
"id": "...",
"sourceId": "...",
"targetId": "...",
"kind": "contains"
},
"sourceNode": {
"id": "...",
"title": "Home",
"kind": "nodebook"
},
"targetNode": {
"id": "...",
"title": "My Notes",
"kind": "notebook"
}
}]/api/edgesCreate an edge. Both nodes must exist in the same buildspace. Duplicate edges return 409 with the existing edge ID.
curl -X POST https://www.relentless.build/api/edges \
-H "Authorization: Bearer rlnt_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"sourceId": "parent-node-id",
"targetId": "child-node-id",
"kind": "contains"
}'/api/edgesDelete an edge. All three query params required: sourceId, targetId, kind.
Fusion — The Collaboration Boundary
The Fusion Node bridges the agent buildspace and the human buildspace. Shared nodes appear here automatically — no events, no webhooks. The query is the sync.
How It Works
- Find the Fusion node: In your skeleton response, look for
systemKey: "fusion" - Query shared content:
GET /api/nodes?parentId={fusionId} - Share your work:
PATCH /api/nodes/:id { "shared": true }
// Fusion query response
{
"nodes": [
{
"id": "...",
"title": "Agent's Research",
"_source": "self"
},
{
"id": "...",
"title": "Human's Brief",
"_source": "linked"
}
]
}Read-only boundary: Agents cannot modify linked nodes. To respond to shared content, create your own node and share it back. Setting shared: false immediately removes the node from the next Fusion query.
System Nodes
System nodes are auto-created nodes with special keys. Retrieve them by key name instead of ID.
/api/system-nodes/:keyReturns the full node object. Creates the system node if it doesn't exist yet. Known keys: home, fusion, video-history.
Buildspaces
/api/buildspacesList buildspaces accessible to the authenticated key. Agent keys see their scoped buildspace and the linked human buildspace. Response includes buildspace limits.
{
"buildspaces": [{
"id": "...",
"name": "My Agent Space",
"type": "agent",
"role": "owner",
"memberCount": 1,
"linkedBuildspaceId": "...",
"linkedBuildspaceName": "Personal"
}],
"limits": {
"personal": { "max": 1, "current": 1, "remaining": 0 },
"team": { "max": 3, "current": 0, "remaining": 3 },
"agent": { "max": 2, "current": 1, "remaining": 1 }
}
}Rate Limits
Agent API keys are rate-limited per key. Human sessions are exempt.
| Action | Limit | Window |
|---|---|---|
| Node creates | 50 | per hour |
| Node updates | 100 | per hour |
| Node reads | 500 | per hour |
| Edge creates | 100 | per hour |
Additional limits
| Resource | Limit | |
|---|---|---|
| Content size | 50,000 characters (JSON serialized, API keys only) | |
| Node count | 500 nodes per agent buildspace | |
| Title length | 500 characters | |
// 429 Too Many Requests
{
"error": "rate_limit_exceeded",
"message": "You've used 50 of your 50 node creates for this hour. The limit resets at 14:00 UTC (12 minutes from now).",
"limit": 50,
"remaining": 0,
"resetAt": "2026-02-05T14:00:00Z",
"retryAfterSeconds": 720
}
// 413 Content Too Large
{
"error": "content_too_large",
"message": "Content exceeds maximum size of 50000 characters (current: 50012, overage: 12)",
"size": 50012,
"maxSize": 50000
}Errors
All errors follow a consistent shape. The message field is always human-readable.
{ error: string; message?: string; details?: unknown }| Code | Meaning | |
|---|---|---|
| 400 | Bad request (validation, missing params, invalid kind) | |
| 401 | Unauthorized (bad key, expired, revoked, wrong buildspace) | |
| 403 | Forbidden (viewer role, blocked endpoint) | |
| 404 | Not found | |
| 409 | Conflict (duplicate edge — returns existing edge ID) | |
| 413 | Content too large (over 50,000 chars, API keys only) | |
| 429 | Rate limited (includes Retry-After header) | |
| 500 | Server error | |
Security
Every layer of the Agent API is designed to prevent abuse and protect data isolation.
Allowlist Auth
Scoped keys are rejected by default on all endpoints. Routes explicitly opt in. New endpoints are locked automatically.
Buildspace Isolation
Each key is locked to one buildspace. Cross-buildspace requests return 401 — no exceptions. Fusion reads are server-mediated.
SHA-256 Key Storage
API keys are stored as SHA-256 hashes, never plaintext. A database breach doesn't expose keys.
Input Validation
Zod schemas validate every request body. UUIDs, kinds, titles, and content sizes are all enforced server-side.
SQL Injection Prevention
Drizzle ORM uses parameterized queries exclusively. User input never touches SQL strings.
Privilege Escalation Prevention
API keys cannot access admin endpoints, manage other keys, read user profiles, or send invites. Ever.
Per-Key Rate Limits
Each API key has independent rate counters via Redis sliding windows. One agent can't exhaust limits for another.
Suspension & Expiry
Every request checks user suspension status. Keys expire after 90 days. Revocation is immediate.
Recommended Agent Workflow
# 1. Map the workspace
GET /api/nodes?depth=skeleton
# 2. Find the Fusion node
# Look for systemKey: "fusion" in the skeleton response
# 3. Check what the human shared
GET /api/nodes?parentId={fusionNodeId}
# 4. Do your work — create nodes, organize with edges
POST /api/nodes { "kind": "notebook", "title": "Research" }
POST /api/edges { "sourceId": "home-id", "targetId": "new-id", "kind": "contains" }
# 5. Share results back through Fusion
PATCH /api/nodes/{id} { "shared": true }