Docs
Build with Stale

REST API

Everything the dashboard does, you can do with curl. Same auth, same roles, same results.

Authentication

Send an API key on every request:

Authorization: Bearer sk_YOUR_KEY

Browsers use a session cookie instead, set when you sign in. A key belongs to one workspace and acts with the member role, so admin-only endpoints (marked below) need a signed-in admin.

Roles

viewerRead-only. Every GET.
memberEverything a viewer can do, plus writes: remember, ingest, resolve, approve, integrations.
adminEverything, plus members, API keys and workspace settings.

Conventions

  • Bodies and responses are JSON. Bodies are validated, and bad input returns 400 with details.
  • Errors look like { "error": "…", "details": … }.
  • 404 not found · 409 conflict (e.g. building on a stale node) · 503 the memory database is unreachable.
  • Ids are up to 64 characters: letters, digits and _ . : -.

A full example

Teach it a fact, change it, and see what went stale:

# 1. register a source curl -X POST $HOST/api/v1/sources -H "Authorization: Bearer $KEY" \ -H "content-type: application/json" \ -d '{"id":"src_sales","name":"Sales call","trust":"trusted"}' # 2. a fact, and something built on it curl -X POST $HOST/api/v1/nodes -H "Authorization: Bearer $KEY" -H "content-type: application/json" \ -d '{"id":"f_price","kind":"fact","content":"Renewal price is $9,000","sourceId":"src_sales"}' curl -X POST $HOST/api/v1/nodes -H "Authorization: Bearer $KEY" -H "content-type: application/json" \ -d '{"id":"d_quote","kind":"decision","content":"Quote Acme at $9,000","dependsOn":["f_price"]}' # 3. the price changes curl -X POST $HOST/api/v1/nodes/f_price/supersede -H "Authorization: Bearer $KEY" \ -H "content-type: application/json" -d '{"content":"Renewal price is $12,000"}' # 4. what do I fix? curl $HOST/api/v1/repair-plan -H "Authorization: Bearer $KEY"

All endpoints

Memory

The graph itself.

EndpointRoleWhat it does
GET /api/v1/graphviewerNodes, edges, sources, tainted ids and stats. What the dashboard draws.
POST /api/v1/nodesmemberAdd a fact, belief, decision or action: { kind, content, dependsOn, sourceId }.
GET /api/v1/nodes/:idviewerOne node.
POST /api/v1/nodes/:id/supersedememberA fact changed. Versions it and cascades staleness.
GET /api/v1/nodes/:id/impactviewerBlast radius, dry run.
GET /api/v1/nodes/:id/whyviewerAncestry and firewall verdict.
GET /api/v1/repair-planviewerStale nodes in root-first layers.
POST /api/v1/nodes/:id/resolvememberRevalidate and rebase a stale node.
GET /api/v1/sourcesviewerList sources.
POST /api/v1/sourcesmemberRegister a source.
POST /api/v1/sources/:id/trustmemberQuarantine or restore: { trust, reason }.
GET /api/v1/eventsviewerThe audit log.

Firewall and approvals

Where a human overrides, and an agent cannot.

EndpointRoleWhat it does
GET /api/v1/actions/:id/checkviewerRun the firewall. POST works too.
POST /api/v1/actions/:id/approvememberA human approves a blocked action: { approver, note }. Not available over MCP.
POST /api/v1/actions/:id/executememberRecord that the action ran. 403 if blocked and unapproved.

Learning and review

Feeding text in, and deciding on what’s risky.

EndpointRoleWhat it does
POST /api/v1/ingestmemberFeed any text. Returns added, confirmed, updated, queued.
GET /api/v1/ingestviewerRecent ingest ledger.
GET /api/v1/reviewviewerThe review queue. Filter with ?status=pending.
POST /api/v1/review/:idmemberApprove or reject a queued change: { decision: "approved" | "rejected" }.

Integrations

Connections to your tools.

EndpointRoleWhat it does
GET /api/v1/integrationsviewerList, with their webhook addresses.
POST /api/v1/integrationsadminCreate one. Secrets are encrypted and shown once.
DELETE /api/v1/integrations/:idadminRemove one.
GET /api/v1/integrations/:id/checkviewerTest the credentials.
GET /api/v1/integrations/:id/syncviewerList what can be synced (channels, repos, projects, pages).
POST /api/v1/integrations/:id/syncadminImport the chosen scope.

Workspace and access

People, keys and settings.

EndpointRoleWhat it does
GET /api/workspaceviewerYour workspace and role.
PATCH /api/workspaceadminRename, or set settings.autoApply to trusted or never.
GET /api/workspace/membersviewerMembers and roles.
POST /api/workspace/membersadminAdd an existing user by email with a role.
DELETE /api/workspace/membersadminRemove a member: ?userId=…. A workspace keeps at least one admin.
GET /api/workspace/keysadminList API keys (prefix only, never the secret).
POST /api/workspace/keysadminCreate a key: { name }. The secret is shown once.
DELETE /api/workspace/keysadminRevoke a key: ?id=….

Plans and billing

Razorpay one-time payments. Only active when the server has Razorpay keys.

EndpointRoleWhat it does
GET /api/billingviewerCurrent plan, paid-until date, limits and this month’s usage.
POST /api/billing/checkoutadminStart a payment for a month or a year of Team: { plan: "team", interval }. Returns what Razorpay Checkout needs.
POST /api/billing/verifyadminConfirm a finished payment by its Razorpay signature, then add the time to the plan.

System

Health and demo helpers.

EndpointRoleWhat it does
GET /api/v1/healthviewerIs the memory database reachable?
POST /api/v1/demo/seedmemberWipe the workspace graph and load the demo scenarios.
POST /api/v1/sampleadminLoad the “Northwind Robotics” sample story into this workspace, streaming progress. Safe to run twice.
DELETE /api/v1/tenantmemberDelete this workspace’s memory graph.

Inbound webhooks

These authenticate with the integration’s own secret instead of an API key:

POST /api/hooks/slack/:idSlack Events API. Requests must carry a valid Slack signature.
POST /api/hooks/email/:idForwarded or parsed email. Header x-stale-secret.
POST /api/hooks/razorpayRazorpay order.paid events: a backup if someone pays and closes the tab. Signed with your webhook secret.
POST /api/hooks/generic/:idAny JSON: { source, author, text }. Header x-stale-secret.