HTTP API
REST API for managing your workspace, agents, content, and mailbox. All endpoints require authentication via bearer token.
Authentication
All requests require an Authorization header with a bearer token. LeftFold accepts two token types:
- API key —
sk_prefix, created in the dashboard or via CLI - OAuth JWT — from Supabase Auth (magic link, Google, or GitHub)
curl https://your-project.supabase.co/functions/v1/http-api/workspace \
-H "Authorization: Bearer sk_your_api_key"The API key resolves to a user, which resolves to a workspace. All queries are scoped to that workspace.
Workspace
Returns the authenticated user's workspace.
List all projects in the workspace.
Create a new project.
{
"name": "Blog",
"slug": "blog",
"description": "Company blog content"
}Aggregates
Register a new aggregate type with traits and field definitions.
{
"type_name": "treatment",
"display_name": "Treatment",
"traits": ["content-bearing", "publishable"],
"fields": [
{ "name": "name", "type": "string", "required": true },
{ "name": "duration_minutes", "type": "number" }
]
}Fetch the full schema for an aggregate type (fields, traits, event schemas).
Run a fold pipeline query. Supports $match, $fold, $project (with computed expressions), $group, $sort, $limit, $lookup.
{
"pipeline": [
{ "$match": { "aggregate_type": "article" } },
{ "$fold": {} },
{ "$project": {
"title": 1,
"days_since_update": { "$daysSince": "$_last_event_at" }
}},
{ "$sort": { "_last_event_at": -1 } },
{ "$limit": 10 }
]
}Fetch the raw event stream for an aggregate (not folded).
Append an event to an aggregate. Triggers the enrichment pipeline and knowledge graph projection.
{
"aggregate_type": "article",
"event_type": "article.created",
"project_id": "p1r2o3j-...",
"payload": {
"title": "Getting Started with LeftFold",
"body": "# Introduction\n...",
"slug": "getting-started"
},
"metadata": { "source": "lab" }
}Search
Semantic search across the knowledge graph. Searches aggregate labels by text similarity.
{
"query": "ACL rehabilitation",
"type": "article",
"project_id": "p1r2o3j-...",
"limit": 5
}Agents
List all agents. Optionally filter by status with ?status=active.
Register a new external agent.
{
"name": "lead",
"description": "Lead agent for orchestrating research tasks",
"subscribed_traits": ["content-bearing"],
"capabilities": { "research": true }
}Fetch an agent's card metadata (name, description, capabilities, traits).
Returns pending, unread, and in-progress task counts per agent. Useful for building agent dashboards and monitoring workload.
Relay
These endpoints power the @leftfold/agents package. You do not need to call them directly unless building a custom agent client.
Exchange an API key for Realtime connection credentials. Returns the WebSocket URL, anon key, channel names for each agent, and heartbeat interval.
{
"url": "wss://your-project.supabase.co/realtime/v1",
"anon_key": "eyJ...",
"channels": [
{
"agent_id": "a1b2c3d4-...",
"name": "summarizer",
"channel": "agent:a1b2c3d4-..."
}
],
"heartbeat_interval": 30000
}Update last_seen_at for connected agents. The @leftfold/agents package calls this every 30 seconds.
{
"agent_ids": ["a1b2c3d4-...", "e5f6g7h8-..."]
}Articles
List articles. Supports ?project_id=, ?limit=, and ?cursor= for pagination.
Get a single article by aggregate ID.
Create an article. This appends a creation event to the event store, projects it, updates the knowledge graph, and dispatches to matching agents.
{
"project_id": "p1r2o3j-...",
"title": "Getting Started with LeftFold",
"body": "# Introduction\n\nLeftFold is a content operations platform...",
"slug": "getting-started-with-leftfold",
"tags": ["tutorial", "onboarding"]
}Publish an article. Triggers publishable-trait agents (structured_data, linker).
Files
Upload a file (base64-encoded in JSON body). Validates against workspace storage quota. Optionally associates with an aggregate.
{
"filename": "hero.png",
"data": "iVBORw0KGgo...",
"mime_type": "image/png",
"aggregate_id": "a1b2c3d4-...",
"aggregate_type": "article",
"project_id": "p1r2o3j-..."
}Generate a signed download URL (valid for 1 hour).
List files. Filter by ?prefix= (workspace/project/type/aggregate path).
Delete a file by path. Pass { "path": "..." } in the body.
Mailbox
List mailbox tasks. Filter with query parameters: ?status=pending, ?assignee_id=, ?assignee_type=agent, ?project_id=, ?source_type=.
Create a new mailbox task. Supports idempotency — if idempotency_key matches an existing task, returns the existing task instead of creating a duplicate.
{
"title": "Review this draft",
"assignee_type": "agent",
"assignee_id": "a1b2c3d4-...",
"priority": "high",
"project_id": "p1r2o3j-...",
"metadata": { "type": "review_request", "draft_id": "..." },
"idempotency_key": "review:draft_abc:v1"
}Get a task with its messages and artifacts.
Actions
Eight interaction endpoints are available on each task:
| Endpoint | Action |
|---|---|
| POST /mailbox/:id/approve | Accept the agent's output |
| POST /mailbox/:id/reject | Reject with optional reason |
| POST /mailbox/:id/edit | Accept with modifications |
| POST /mailbox/:id/defer | Set aside for later |
| POST /mailbox/:id/ask | Ask the agent a follow-up question |
| POST /mailbox/:id/assign | Reassign to a different agent or user |
| POST /mailbox/:id/read | Mark as read |
| POST /mailbox/:id/unread | Mark as unread |
Errors
All errors return a JSON body with an error field:
{
"error": "agent 'conductor' not found in this workspace"
}| Status | Meaning |
|---|---|
| 400 | Bad request — missing required fields or invalid input |
| 401 | Missing or invalid bearer token |
| 402 | Payment required — scope needs an active subscription |
| 403 | Forbidden — token does not have the required scope |
| 404 | Resource not found |
| 409 | Conflict — task not in expected state |
| 500 | Internal server error |