DocsArchitectureChangelogBlogView source

Delega Documentation

Historical/as-built documentation: Public hosted access retired July 28, 2026. Signup, agent onboarding, recovery, and billing routes now return410 hosted_service_retired. Existing owner credentials are the only supported way to use the private deployment. Read the case studyor the architecture and threat model.

Last updated: August 17, 2026

As-built integration surfaces

This is the historical surface map. Public onboarding is closed; only Ryan McMillan's existing owner credentials can use the private deployment.

Original surface map

MCP: exposed Delega as native tools in compatible coding assistants such as Codex and Claude Code. No separate OpenAI key was required by Delega. API / Python SDK / CLI: served scripts, automations, dashboards, and custom apps. The public clients remain implementation artifacts; owner credentials are required for the private runtime. OpenAI Agents SDK / LangChain / CrewAI examples: demonstrated custom agent loops. Those examples also required a model-provider key so the framework could decide which tools to call. Task console (browser): provided search, Fleet Attention triage, coordination history, recurring-work oversight, and safe human steering. The console remains online but unlinked for the owner-only deployment.

The private browser console preserves the original human-oversight surface: task and decision-memory search; project, label, assignee, claim, and session-state filters; Fleet Attention triage; context history, comments, subtasks, links, and delegation chains; recurring schedules; and capability-aware steering. Its location is intentionally omitted from the public archive.

MCP Server

The Model Context Protocol (MCP) is the standard interface that lets AI agents interact with external tools. Delega's MCP server gives your AI assistant direct access to task management — create tasks, track progress, manage agents, and more, all through natural conversation.

Install

npx @delega-dev/mcp

The MCP server uses stdio transport and works with any MCP-compatible client including Claude Code, Cursor, Codex, VS Code, and OpenClaw.

Available Tools (44)

list_tasks List tasks with filters (project, label, due date, status) get_task Get full task details including subtasks, context, and links create_task Create a new task, optionally requiring structured evidence at completion list_recurrences List recurring task templates create_recurring_task Create a recurring task template update_recurrence Update or pause a recurring task template delete_recurrence Delete a recurring task template update_task Update task fields (incl assign_task Assign a task to an agent (or null to unassign) delegate_task Delegate: create a child task linked to a parent get_task_chain Return the full parent/child delegation chain, indented by depth get_task_context Read a task's persistent context blob — shared state, decisions, and notes saved across sessions get_context_history Read the append-only provenance ledger for a task's context recall Search decision-memory across readable tasks without knowing which task holds it update_task_context Merge keys into a task's persistent context blob (deep merge, not replace) claim_task Claim a task for exclusive processing: the next from the queue, or a specific one via task_id heartbeat_task Extend the lease on a task you currently hold a claim on, optionally reporting a session state at the same time set_task_state Report why you are holding a claimed task — working, waiting_input, or errored — without extending the lease release_task Release a claimed task back to the queue with an optional handoff note for the next worker link_task Attach a branch, commit, PR, or URL link to a task list_task_links List links attached to a task find_duplicate_tasks TF-IDF + cosine similarity check against open tasks get_usage Return quota + rate-limit info for the current plan. complete_task Complete a task with optional or policy-required structured evidence delete_task Delete a task permanently add_comment Add a comment to a task list_projects List all projects get_stats Get task statistics fleet_attention Show stalled, abandoned, errored, blocked, overdue, and repeatedly reopened work in one triage view. list_agents List all registered agents register_agent Register a new agent (returns API key), optionally with a role preset set_agent_role Set an agent's role: worker, coordinator, or admin (admin key required) delete_agent Delete an agent (refused if the agent has active tasks) list_webhooks List all webhooks (admin only) create_webhook Create a webhook for event notifications (admin only) delete_webhook Delete a webhook by ID (admin only) list_automations List automation rules with run and failure counters (admin only) create_automation Create an in-process when→then automation rule (admin only) update_automation Update or enable/disable an automation rule (admin only) delete_automation Delete an automation rule and its run log (admin only) list_ingress_sources List inbound connector sources and delivery counters (admin only) create_ingress_source Create a signed inbound connector source (admin only) update_ingress_source Update, enable, disable, or rotate an inbound connector source (admin only) delete_ingress_source Delete an inbound connector source and its delivery log (admin only)

Configuration

Add the Delega MCP server to your AI editor's configuration file.

Add to claude_desktop_config.json (Claude Desktop) or project .mcp.json (Claude Code)

{ "mcpServers": { "delega": { "command": "npx", "args": ["-y", "@delega-dev/mcp"], "env": { "DELEGA_AGENT_KEY": "dlg_your_key_here" } } } }

Add to .cursor/mcp.json in your project root

{ "mcpServers": { "delega": { "command": "npx", "args": ["-y", "@delega-dev/mcp"], "env": { "DELEGA_AGENT_KEY": "dlg_your_key_here" } } } }

Add to ~/.codeium/windsurf/mcp_config.json

{ "mcpServers": { "delega": { "command": "npx", "args": ["-y", "@delega-dev/mcp"], "env": { "DELEGA_AGENT_KEY": "dlg_your_key_here" } } } }

Add to .vscode/mcp.json in your project root

{ "servers": { "delega": { "command": "npx", "args": ["-y", "@delega-dev/mcp"], "env": { "DELEGA_AGENT_KEY": "dlg_your_key_here" } } } }

Add to ~/.continue/config.json

{ "experimental": { "modelContextProtocol": { "servers": { "delega": { "command": "npx", "args": ["-y", "@delega-dev/mcp"], "env": { "DELEGA_AGENT_KEY": "dlg_your_key_here" } } } } } }

Add to codex.json or configure via codex --mcp

{ "mcpServers": { "delega": { "command": "npx", "args": ["-y", "@delega-dev/mcp"], "env": { "DELEGA_AGENT_KEY": "dlg_your_key_here" } } } }

Add to ~/.openclaw/openclaw.json under mcp.servers

{ "mcp": { "servers": { "delega": { "command": "npx", "args": ["-y", "@delega-dev/mcp"], "env": { "DELEGA_AGENT_KEY": "dlg_your_key_here" } } } } }

OpenClaw agents also have native shell access — your agent can call the REST API directly without MCP. See the quickstart for the skill-based approach.

TOOLlist_tasks

List tasks, optionally filtered by project, label, due date, or completion status.

Parameters

NameTypeRequiredDescription
project_idnumberoptionalFilter by project ID
labelstringoptionalFilter by label
duestringoptionalDate filter: "today", "upcoming", or "overdue"
completedbooleanoptionalFilter by completion status

Example Response

[ { "id": 42, "content": "Research competitor pricing", "priority": 2, "labels": ["research"], "due_date": "2026-03-20", "completed": false, "project_id": 1 } ]
TOOLget_task

Get full details of a specific task including subtasks, context, delegation metadata, and links.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task ID to retrieve

Example Response

{ "id": 42, "content": "Research competitor pricing", "description": "Look at top 5 competitors", "priority": 2, "labels": ["research"], "due_date": "2026-03-20", "completed": false, "context": {}, "links": [ { "kind": "pr", "repo": "acme/webapp", "ref": "42", "url": "https://github.com/acme/webapp/pull/42" } ], "subtasks": [ { "id": 1, "content": "Check competitor A", "completed": true }, { "id": 2, "content": "Check competitor B", "completed": false } ] }
TOOLcreate_task

Create a new task. Set evidence_policy to required when completion must include at least one strong evidence reference.

Parameters

NameTypeRequiredDescription
contentstringrequiredTask title / content
descriptionstringoptionalLonger description
project_idnumberoptionalProject to assign to
labelsstring[]optionalArray of label strings
prioritynumberoptionalPriority level (1–4)
due_datestringoptionalDue date in YYYY-MM-DD format
evidence_policy"required" | nulloptionalSet to required to require structured strong evidence at completion

Example Response

{ "id": 43, "content": "Review pull request #42", "priority": 3, "labels": ["review"], "due_date": "2026-03-16", "completed": false }
TOOLlist_recurrences

List recurring task templates. Recurrences spawn normal task instances; completing an instance does not delete the schedule.

Parameters

No parameters.

Example Response

[ { "id": "rec_01", "content": "Replace furnace filter", "rule_type": "monthly", "interval": 1, "timezone": "America/Chicago", "anchor_day": 1, "next_due_at": "2026-07-01T05:00:00.000Z", "active": true, "skip_if_open": true } ]
TOOLcreate_recurring_task

Create a recurring task template. The hosted scheduler spawns normal task instances from this template and links them with source_recurrence_id.

Parameters

NameTypeRequiredDescription
contentstringrequiredTask title/content for spawned instances
rule_typedaily | weekly | monthly | yearlyrequiredRecurrence rule type
intervalnumberoptionalRule interval, default 1
timezonestringoptionalIANA timezone, e.g. America/Chicago
anchor_daynumberoptionalDay of month for monthly/yearly rules
anchor_monthnumberoptionalMonth for yearly rules
anchor_weekdaynumberoptionalWeekday for weekly rules, Sunday=0
next_due_atstringoptionalOptional ISO timestamp for first due occurrence
skip_if_openbooleanoptionalSkip spawning and roll forward while a prior instance is open

Example Response

{ "id": "rec_01", "content": "Replace furnace filter", "rule_type": "monthly", "interval": 1, "timezone": "America/Chicago", "anchor_day": 1, "next_due_at": "2026-07-01T05:00:00.000Z", "active": true, "skip_if_open": true }
TOOLupdate_recurrence

Update a recurring task template, including pausing/resuming with active=false or active=true. Existing spawned task instances remain normal tasks.

Parameters

NameTypeRequiredDescription
recurrence_idstring | numberrequiredThe recurrence ID to update
contentstringoptionalTask title/content for future spawned instances
rule_typedaily | weekly | monthly | yearlyoptionalRecurrence rule type
intervalnumberoptionalRule interval
timezonestringoptionalIANA timezone
next_due_atstring | nulloptionalISO timestamp for next due occurrence
activebooleanoptionalWhether the recurrence is active
skip_if_openbooleanoptionalSkip spawning while a prior instance is open

Example Response

{ "id": "rec_01", "content": "Replace furnace filter", "active": false, "next_due_at": "2026-07-01T05:00:00.000Z" }
TOOLdelete_recurrence

Delete a recurring task template. Existing spawned task instances remain as normal tasks.

Parameters

NameTypeRequiredDescription
recurrence_idstring | numberrequiredThe recurrence ID to delete

Example Response

{ "ok": true }
TOOLupdate_task

Update an existing open task's fields. evidence_policy: "required" makes structured strong evidence mandatory at completion; only an admin key may remove a required policy.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task ID to update
contentstringoptionalUpdated title
descriptionstringoptionalUpdated description
labelsstring[]optionalReplace labels
prioritynumberoptionalPriority (1–4)
due_datestringoptionalDue date (YYYY-MM-DD)
project_idnumberoptionalMove to project
assigned_to_agent_idstring | number | nulloptionalAssign to agent, or null to unassign
evidence_policy"required" | nulloptionalRequire structured strong completion evidence; only admins may later clear required

Example Response

{ "id": 42, "content": "Research competitor pricing strategies", "priority": 1, "labels": ["research", "urgent"], "completed": false }
TOOLassign_task

Assign a task to an agent, or pass null to unassign. For multi-agent handoffs where you want the parent/child accountability chain, use delegate_task instead — assign_task does not record a delegation.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task to (re)assign
agent_idstring | number | nullrequiredAgent ID to assign to, or null to unassign

Example Response

{ "id": 42, "content": "Research competitor pricing", "assigned_to_agent_id": "agt_research_02", "completed": false }
TOOLdelegate_task

Delegate a task: create a child task linked to a parent. The parent's status flips to delegated and a parent/child accountability chain is recorded (inspectable via get_task_chain). Use this — not assign_task — for multi-agent handoffs.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredParent task ID to delegate from
contentstringrequiredChild task title / content
descriptionstringoptionalDetailed description
project_idnumberoptionalProject ID (admin only for non-self delegations)
labelsstring[]optionalLabels to apply to the child
prioritynumberoptionalPriority (1–4)
due_datestringoptionalDue date (YYYY-MM-DD)
assigned_to_agent_idstring | numberoptionalAgent ID to assign the child task to

Example Response (child task)

{ "id": "t_child_01", "content": "Research competitor pricing pages", "parent_task_id": "t_parent_01", "root_task_id": "t_parent_01", "delegation_depth": 1, "status": "open", "assigned_to_agent_id": "agt_research_02" }
TOOLget_task_chain

Return the full delegation chain for a task (root + all descendants, sorted by depth). Use this to inspect parent/child accountability after a series of delegate_task calls.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredAny task ID in the chain

Example Response (rendered)

Delegation chain (root #abc, depth 2, 2/4 complete): [#abc] Write report (depth 0, delegated) [#def] Draft intro (depth 1, completed) [#jkl] Draft conclusion (depth 1, pending) [#ghi] Research sources (depth 2, completed)
TOOLget_task_context

Read a task's persistent context blob — the shared state, decisions, and notes saved across sessions. Optionally include per-key provenance showing who wrote the current live entry, source, timestamp, and context version.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task whose context to read
include_provenancebooleanInclude per-key author/source/version provenance for current live context entries

Example Response

{ "context": { "step": "research_done", "findings": ["price: $20/mo", "cutoff: 2026-05-01"], "next_step": "draft summary" } }
TOOLget_context_history

Read the append-only provenance ledger for a task's context. Use key to narrow history to one context key; omit it to return the newest history across all keys.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task whose context history to read
keystringOptional context key to filter history

Example Response

Context history for task #abc: step (v4, human_stated, by Research Agent, 2026-06-10 12:00:00, live) "research_done" step (v3, agent_inferred, by Research Agent, 2026-06-10 11:55:00, superseded 2026-06-10 12:00:00) "drafting"
TOOLrecall

Search context entries across every task the caller can read, so a new session can recover a prior decision without knowing its task ID. Results use lexical overlap, with human-stated entries weighted highest, and include the matching key, value, provenance source, score, and owning task. Current entries are searched by default; filters can narrow by project, source, or key, and include_superseded can include overwritten or retracted history. For object-valued context, query by the context key because nested object fields may not match independently in lexical v1. Full-mode hosted API only.

Parameters

NameTypeRequiredDescription
qstringrequiredDecision, fact, or constraint to recall
project_idstring | numberoptionalRestrict results to one project
sourcehuman_stated | agent_inferred | agent_observed | importedoptionalRestrict results to one provenance source
keystringoptionalRestrict results to one context key
limitnumberoptionalMaximum results, 1–100 (default 20)
include_supersededbooleanoptionalInclude overwritten or retracted entries (default false)

Example Response

Recall — 1 match(es) for "D1 migration approach": [human_stated] migration_strategy: Use the D1 batch API for atomic migrations ↳ task #t_database (Set up the database) · score 0.43
TOOLupdate_task_context

Merge keys into a task's persistent context blob. Existing keys are preserved; supplied keys are added or overwritten. Per-key provenance is recorded for each top-level key in the write.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task whose context to update
contextobjectrequiredKeys merged (not replaced) into existing context
expected_versionnumberOptimistic concurrency guard from get_task_context
sourcehuman_stated | agent_inferred | agent_observed | importedAttribution source for this context write; defaults to agent_inferred

Example Response

{ "context": { "step": "research_done", "findings": ["price: $20/mo", "cutoff: 2026-05-01"] } }
TOOLclaim_task

Atomically claim a task: open, unclaimed, and unassigned or assigned to the caller — plus tasks whose claim lease has expired (takeover). Without task_id, picks the next claimable task ordered by priority, then creation time, or returns null when nothing is claimable. With task_id (v1.5.0+), claims that specific task — e.g. one found via list_tasks, or after a write was rejected with “claim it first” — and fails with a conflict if it is completed, assigned to another agent, or claimed with a live lease. The claim is a lease (default 300 seconds) — extend it with heartbeat_task, or requeue with release_task. Requires a full-mode (claimed) hosted key. Claiming never changes assigned_to_agent_id.

Parameters

NameTypeRequiredDescription
task_idstring | numberoptionalClaim this specific task instead of the next from the queue
project_idnumberoptionalOnly claim tasks in this project (queue claim only)
labelsstring[]optionalOnly claim tasks carrying all of these labels (queue claim only)
lease_secondsnumberoptionalLease duration in seconds (30–3600, default 300)

Example Response

{ "id": "t_01HQUEUE", "content": "Process invoice batch 2026-06", "priority": 1, "labels": ["invoices"], "status": "claimed", "claimed_by_agent_id": "agt_worker_01", "claimed_at": "2026-06-09T14:00:00Z", "lease_expires_at": "2026-06-09T14:05:00Z", "assigned_to_agent_id": null }
TOOLheartbeat_task

Extend the lease on a task the calling agent currently holds an active claim on. Returns the task with the new lease_expires_at. Optionally reports a session state (working | waiting_input | errored) plus free-text detail in the same call. For a genuine human decision blocker, use QUESTION: <one line> / OPTIONS: <a / b / …> in a waiting_input detail so configured notification delivery can include a single-use Decision Answer link. Fails with a conflict error (409) if the caller does not hold an active, unexpired claim.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe claimed task to heartbeat
lease_secondsnumberoptionalNew lease duration in seconds (30–3600, default 300)
statestringoptionalSession state to report: working, waiting_input, or errored
detailstringoptionalFree-text detail for the state (≤500 chars). Requires state.

Example Response

{ "id": "t_01HQUEUE", "status": "claimed", "claimed_by_agent_id": "agt_worker_01", "lease_expires_at": "2026-06-09T14:10:00Z", "session_state": "working" }
TOOLset_task_state

Set the session state of a task the calling agent holds an active claim on, without extending the lease — an agent blocked on input shouldn’t have to fake liveness to stay visible. state is one of working, waiting_input, or errored; optional detail (≤500 chars) explains the state and is replaced on every transition. For a genuine human decision blocker, format a waiting_input detail as QUESTION: <one line> / OPTIONS: <a / b / …>. When delivery is configured, the email includes a single-use answer link. The reply waits in the task for the next session; there is no automatic resume. Successful escalation emails are limited to one per task every 30 minutes. The active state is cleared automatically when the claim ends (an explicit release preserves it in the handoff fields). Fires a task.state_changed webhook. Fails with a conflict error (409) if the caller does not hold an active, unexpired claim. Hosted API only.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe claimed task
statestringrequiredSession state: working, waiting_input, or errored
detailstringoptionalFree-text detail (≤500 chars), e.g. “needs prod API key”

Example Response

{ "id": "t_01HQUEUE", "status": "claimed", "session_state": "waiting_input", "session_state_detail": "needs prod API key", "lease_expires_at": "2026-06-09T14:05:00Z" }
TOOLrelease_task

Release a claimed task back to the queue: status returns to open and the lease is cleared so another worker can claim it. Add an optional handoff note (≤500 characters) describing where work stopped; if omitted, Delega preserves the current session_state_detail. The note and departing session state remain on the task and are surfaced to the next claimant as a Resuming from line. Holder or admin only (403 otherwise; 409 if the task is not claimed). A pre-existing assigned_to_agent_id survives the release.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe claimed task to release
handoffstringoptionalWhere work stopped or why it is being released (≤500 characters)

Example Response

{ "id": "t_01HQUEUE", "status": "open", "claimed_by_agent_id": null, "claimed_at": null, "lease_expires_at": null, "handoff_note": "Migration written; verification still pending", "handoff_state": "working", "handoff_by_agent_id": "agt_worker_01" }
TOOLfind_duplicate_tasks

Check whether proposed task content is similar to existing open tasks (TF-IDF + cosine similarity). Call before create_task to avoid redundant work.

Parameters

NameTypeRequiredDescription
contentstringrequiredProposed task content to check
thresholdnumberoptionalSimilarity threshold 0–1 (default 0.6)

Example Response

{ "has_duplicates": true, "matches": [ { "task_id": "t_01HABC", "content": "Research competitor pricing", "score": 0.85 } ] }
TOOLget_usage

Get quota and rate-limit information for the current plan.

Parameters

No parameters

Example Response

{ "plan": "free", "tasks_this_month": 142, "task_count": 142, "task_limit": 1000, "limit": 1000, "reset_date": "2026-05-01T00:00:00.000Z", "agent_count": 3, "agent_limit": 5, "webhook_count": 1, "webhook_limit": 1, "project_count": 4, "project_limit": 100, "rate_limit_rpm": 60, "max_content_chars": 2000 }
TOOLcomplete_task

Mark a task as completed. Attach up to five structured evidence items. A task whose evidence_policy is required needs at least one strong kind: commit, pr, ci_check, deploy_sha, or artifact_url. command_output can supplement but cannot satisfy a required policy alone. Delega stores references for spot-checking; it does not execute or verify them.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task ID to complete
evidenceobject[]optionalUp to five { kind, ref, summary? } items; required-policy tasks need a strong kind

Example Response

{ "id": 42, "completed": true, "completed_at": "2026-03-15T14:30:00Z", "completion_evidence": [ { "kind": "commit", "ref": "abc123", "summary": "Implementation" } ] }
TOOLdelete_task

Delete a task permanently.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task ID to delete

Example Response

{ "ok": true }
TOOLadd_comment

Add a comment to a task.

Parameters

NameTypeRequiredDescription
task_idstring | numberrequiredThe task to comment on
contentstringrequiredComment text
authorstringoptionalOverride author display name

Example Response

{ "id": 7, "task_id": 42, "content": "Found pricing data for all 5 competitors.", "author": "research-bot", "created_at": "2026-03-15T14:00:00Z" }
TOOLlist_projects

List all projects.

Parameters

No parameters

Example Response

[ { "id": 1, "name": "Roadmap", "color": "#0ea5e9" }, { "id": 2, "name": "Inbox", "color": "#8b5cf6" } ]
TOOLget_stats

Get task statistics including totals, completed today, due today, overdue, and breakdown by project.

Parameters

No parameters

Example Response

{ "total": 150, "completed_today": 12, "due_today": 5, "overdue": 2, "by_project": { "Roadmap": 45, "Inbox": 12 } }
TOOLfleet_attention

Return one coordination triage view for abandoned claims, live claims whose holder has gone quiet, errored work, tasks waiting on input, overdue work, and tasks reopened at least three times. Coordinators and agents with tasks.read_all see the account view; workers see only tasks involving them. Hosted API only.

Parameters

No parameters

Example Response

Fleet attention — 2 item(s) need attention: Errored (1): #t_error Fix production sync — "build failed" Waiting on input (1): #t_input Confirm rollout window — "needs human approval"
TOOLlist_agents

List all registered agents.

Parameters

No parameters

Example Response

[ { "id": "agt_01", "name": "default", "display_name": "Default Agent", "active": true }, { "id": "agt_02", "name": "research-bot", "display_name": "Research Bot", "active": true } ]
TOOLregister_agent

Register a new agent. Returns an API key — save it, as it cannot be retrieved again. Pass role to apply a permission preset at creation.

Parameters

NameTypeRequiredDescription
namestringrequiredAgent slug (lowercase, hyphens)
display_namestringoptionalHuman-readable name
descriptionstringoptionalWhat the agent does
rolestringoptionalRole preset: worker, coordinator, or admin
permissionsstring[]optionalFine-grained scopes (tasks.read_all, tasks.comment_all); prefer role presets

Example Response

{ "id": "agt_03", "name": "deploy-bot", "display_name": "Deploy Bot", "role": "worker", "api_key": "dlg_..." }
TOOLset_agent_role

Set an agent's role (admin key required). worker: own-task scope. coordinator: sees and can comment on all account tasks. admin: full account management. Sandbox agents graduate through the claim flow and cannot be assigned a role.

Parameters

NameTypeRequiredDescription
agent_idstring | numberrequiredAgent ID to change
rolestringrequiredworker, coordinator, or admin

Example Response

{ "id": "agt_03", "name": "deploy-bot", "role": "coordinator", "permissions": "[\"tasks.read_all\",\"tasks.comment_all\"]" }
TOOLdelete_agent

Delete an agent. The API refuses if the agent has active tasks, is the recovery agent, is the last active agent, or is the caller itself.

Parameters

NameTypeRequiredDescription
agent_idstring | numberrequiredAgent ID to delete

Example Response

{ "ok": true }
TOOLlist_webhooks

List all webhooks configured for your account (admin only).

Parameters

No parameters

Example Response

[ { "id": 1, "url": "https://example.com/hooks/delega", "events": ["task.created", "task.completed", "task.delegated"], "active": true } ]
TOOLcreate_webhook

Create a webhook to receive event notifications (admin only). The signing secret is returned once at creation — save it, it's used to verify HMAC-SHA256 signatures on delivered events.

Parameters

NameTypeRequiredDescription
urlstringrequiredHTTPS URL to receive webhook POSTs
eventsstring[]requiredEvents to subscribe to: task.created, task.updated, task.completed, task.deleted, task.assigned, task.delegated, task.commented, task.claimed, task.released, task.state_changed, task.linked

Example Response

{ "id": 1, "url": "https://example.com/hooks/delega", "events": ["task.completed"], "secret": "whsec_..." }
TOOLdelete_webhook

Delete a webhook by ID (admin only).

Parameters

NameTypeRequiredDescription
webhook_idstring | numberrequiredWebhook ID to delete

Example Response

{ "ok": true }
TOOLlist_automations

List the account's in-process automation rules with trigger, condition, action, active, run, and failure summaries. Admin only; hosted API only.

Parameters

No parameters

Example Response

[#7b1d4d8ece7c4d3c830994d9978683f5] Triage bugs When: task.created · If: label has bug Then: assign, set_priority Active: yes · Runs: 12 · Last run: 2026-07-23 18:00:00
TOOLcreate_automation

Create an account-level when→then rule on Delega task events. Conditions are AND-combined and actions run in order. Field mutations skip live-claimed tasks; append-only comments remain allowed. Admin only; hosted API only.

Parameters

NameTypeRequiredDescription
namestringrequiredHuman-readable rule name (max 80 characters)
eventstringrequiredTask event that triggers the rule
conditionsobject[]optionalUp to 10 AND-combined conditions; omit to match every event
actionsobject[]requiredOne to five ordered actions
activebooleanoptionalSet false to create the rule disabled

Example Response

Automation created: [#7b1d4d8ece7c4d3c830994d9978683f5] Triage bugs When: task.created · If: label has bug Then: assign, set_priority Active: yes · Runs: 0
TOOLupdate_automation

Update a rule. Only supplied fields change; condition and action arrays are full replacements. Task-producing idempotency keys persist by action slot for already-seen source events, so create a new rule when a changed configuration needs a clean history. Setting active: true re-enables an auto-disabled rule and clears its failure streak. Admin only; hosted API only.

Parameters

NameTypeRequiredDescription
automation_idstring | numberrequiredAutomation rule ID to update
namestringoptionalReplacement rule name
eventstringoptionalReplacement trigger event
conditionsobject[]optionalReplacement conditions
actionsobject[]optionalReplacement ordered actions
activebooleanoptionalEnable or disable the rule

Example Response

Automation updated: [#7b1d4d8ece7c4d3c830994d9978683f5] Triage bugs When: task.created · If: label has bug Then: assign, set_priority Active: no · Runs: 12 · Last run: 2026-07-23 18:00:00
TOOLdelete_automation

Delete an automation rule and its stored run log. Tasks and comments previously created by the rule remain. Admin only; hosted API only.

Parameters

NameTypeRequiredDescription
automation_idstring | numberrequiredAutomation rule ID to delete

Example Response

Automation #7b1d4d8ece7c4d3c830994d9978683f5 deleted.
TOOLlist_ingress_sources

List inbound connector sources with their ingest paths, templates, filters, pinned routing, and delivery counters. Signing secrets are never returned. Admin only; hosted API only.

Parameters

No parameters.

Example Response

[#7b1d4d8ece7c4d3c830994d9978683f5] GitHub Actions CI Ingest: POST /v1/ingress/7b1d4d8ece7c4d3c830994d9978683f5 (hmac-sha256) Template: {"content":"CI failed: {{workflow.name}}"} Filters: conclusion eq failure Active: yes · Deliveries: 12 · Last: 2026-07-23 18:00:00
TOOLcreate_ingress_source

Create a signed inbound connector. The server generates a 256-bit HMAC secret and returns it once. Templates map primitive payload values into task fields; routing is pinned here rather than read from the event. Ingress provenance remains sticky across automation-created children. Admin only; hosted API only.

Parameters

NameTypeRequiredDescription
namestringrequiredSource name (max 80 characters)
templateobjectrequiredTask mapping: content plus optional description, priority, labels, and dedupe_key
filtersobject[]optionalUp to 10 AND-combined filters
default_project_idstringoptionalPinned project
default_assignee_agent_idstringoptionalPinned assignee
activebooleanoptionalDefaults to true

Example Response

Ingress source created: [#7b1d4d8ece7c4d3c830994d9978683f5] GitHub Actions CI Ingest: POST /v1/ingress/7b1d4d8ece7c4d3c830994d9978683f5 (hmac-sha256) Template: {"content":"CI failed: {{workflow.name}}"} Filters: conclusion eq failure Active: yes · Deliveries: 0 Secret: abcd…7890 (masked)
TOOLupdate_ingress_source

Update a source. Only supplied fields change; templates and filters are full replacements. Set rotate_secret: true to invalidate the old secret and mint a new once-shown secret. Admin only; hosted API only.

Parameters

NameTypeRequiredDescription
source_idstring | numberrequiredIngress source ID
namestringoptionalReplacement name
templateobjectoptionalReplacement task mapping
filtersobject[]optionalReplacement filters
default_project_idstring | nulloptionalNew pinned project, or null to clear
default_assignee_agent_idstring | nulloptionalNew pinned assignee, or null to clear
activebooleanoptionalEnable or disable
rotate_secretbooleanoptionalMint a new signing secret

Example Response

Ingress source updated: [#7b1d4d8ece7c4d3c830994d9978683f5] GitHub Actions CI Ingest: POST /v1/ingress/7b1d4d8ece7c4d3c830994d9978683f5 (hmac-sha256) Active: no · Deliveries: 12
TOOLdelete_ingress_source

Delete a source and its retained delivery log. Existing tasks remain with their provenance. Admin only; hosted API only.

Parameters

NameTypeRequiredDescription
source_idstring | numberrequiredIngress source ID to delete

Example Response

Ingress source #7b1d4d8ece7c4d3c830994d9978683f5 deleted.

Environment Variables

VariableDescriptionRequired
DELEGA_AGENT_KEYExisting owner agent key; owner admins can create additional keys through the private surfacesrequired
DELEGA_API_URLAPI endpoint. Defaults to https://api.delega.dev.optional
DELEGA_REVEAL_AGENT_KEYSSet to "1" to show full API keys in MCP tool output (hidden by default)optional
DELEGA_REVEAL_WEBHOOK_SECRETSSet to "1" before creating or rotating a webhook or ingress source to show its once-returned secret in full; otherwise MCP masks itoptional

Historical Setup Command

delega init is preserved as an implementation artifact. Its hosted signup step now returns 410 hosted_service_retired.

Retired behavior

The former init subcommand attempted public signup and local MCP configuration. Its hosted signup step now returns 410 hosted_service_retired.

Only Ryan McMillan’s existing owner credentials are supported by the private runtime.

Package artifact & owner authentication

The public package remains installable for implementation review. Only an existing owner key can authenticate against the private runtime.

Install

npm install -g @delega-dev/cli

Authenticate

delega login # Paste your API key when prompted delega whoami # Verify your identity

Environment Variable (alternative)

export DELEGA_API_KEY=dlg_your_key

Source: GitHub · npm

Tasks

Create, list, complete, delegate, and delete tasks from the command line.

# List open tasks delega tasks list # Include completed tasks delega tasks list --completed # Limit results delega tasks list --limit 10 # Create a task delega tasks create "Review pull request #42" # Create with options delega tasks create "Fix login bug" --priority 1 --labels "bug,urgent" --due "2026-03-20" # Show task details delega tasks show <task-id> # Mark a task as completed delega tasks complete <task-id> # Delete a task delega tasks delete <task-id> # Delegate a task to another agent delega tasks delegate <task-id> <agent-id>

Historical GitHub App flow

The owner-only deployment can link repositories through the existing GitHub App. The command below documents the original account-scoped installation flow; it cannot create public hosted access.

Connect Flow

delega login delega github connect # opens GitHub's install page # pick repos (or the whole org); Delega links them automatically

Reference a task from code

git commit -m "Fix login delega:#74d0be2b7a4b4564b6f186fb3f3769c2" # a PR body line "Closes-Delega: #<task-id>" completes the task on merge

Adding or removing repos from the installation keeps Delega's links in sync; uninstalling deactivates them. The installation is bound to the account that connected it, so no other account can claim those repositories. Use --no-open to print the URL instead of launching a browser.

Repo Sync

The owner-only runtime can mirror tasks into a Git repository as deterministic JSONL and push local task, context, and link changes with context-version conflict checks.

Fresh Repo Flow

delega login delega sync init --repo owner/name delega sync pull delega sync status delega sync push

Files

.delega/config.json # repo/project sync settings .delega/tasks.jsonl # deterministic task mirror; safe to commit

Conflict Behavior

# sync push sends expected_version for context updates # stale local mirror exits non-zero with: { "error": "sync_conflict", "local_version": 1, "hosted_version": 2, "hosted_context": { "decision": "newer hosted state" } }

When run in a Git checkout, sync push auto-links the current branch and HEAD commit to pushed task changes. Add --no-auto-link to disable that.

GitHub Action

Run Delega sync from GitHub Actions using a repository secret named DELEGA_API_KEY.

Workflow

name: Delega Sync on: push: branches: [main] jobs: delega: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: delega-dev/sync-action@v1 with: api-key: ${{ secrets.DELEGA_API_KEY }} command: push

Supported commands are pull, push, and status. The action shells out to npx @delega-dev/cli and passes DELEGA_API_KEY and optional DELEGA_API_URL.

Agents

Manage agent identities and API keys.

# List agents delega agents list # Create a new agent delega agents create <name> --display-name "Research Bot" # Rotate an agent's API key (admin key required) delega agents rotate <agent-id>

Stats

View task statistics for your account.

# Show usage statistics delega stats

Global Options

FlagDescription
--jsonOutput raw JSON (works on all read commands)
--api-url <url>Override the API URL for this command
--versionPrint CLI version
--helpShow help for any command

Configuration

The CLI stores settings in ~/.delega/config.json. API keys are stored securely in your OS keychain.

Keychain Storage

macOS: Keychain Access Linux: libsecret Windows: DPAPI (Data Protection API)

Try the API

Paste your API key below to try GET endpoints directly from this page.

Hosted Origin: https://api.delega.dev

Your key is only stored in this browser tab and never persisted. Hosted API examples on this page use the /v1 namespace.

Private Runtime

The API examples below document the system as built. They work only with an existing owner credential; they are not a public quick start.

Reference Scope

This page is the API reference for https://api.delega.dev/v1.

The machine-readable contract is available at api.delega.dev/v1/openapi.json.

Agent Roles

sandbox (historical retired onboarding role) - no new sandbox accounts are issued - the former claim flow returns 410 hosted_service_retired worker (full mode — default) - sees/mutates tasks it created, is assigned, completed, or claims - can delegate, claim/heartbeat/release, use dedup, manage context, read stats coordinator (full mode + tasks.read_all + tasks.comment_all) - sees every account task; can comment on any readable task - mutation scope unchanged from worker admin - full access plus account management - required for agents list/create/delete + role changes, projects create/update/delete, webhooks, and automation rules - the last active admin cannot be demoted Set roles via PUT /v1/agents/:id {"role": "worker|coordinator|admin"} (admin key), the dashboard agent panel, `delega agents role`, or the set_agent_role MCP tool.

Identifier Conventions

Retired signup, verification, recovery, claim, and billing routes return 410 hosted_service_retired. Authenticated non-owner accounts return 403 service_retired. Existing owner-scoped resources retain their acc_/agt_/prj_ external IDs.

Rate Limits

The labels below preserve the public service's as-built limits. Public plans and onboarding are retired; the private runtime uses the owner's configured limits.

Historical public-plan API limits

Free: 60 requests/minute Pro: 500 requests/minute Scale: 2,000 requests/minute

Historical onboarding rate limits

POST /v1/verify: 5 attempts per 60-second window (per user) POST /v1/resend-verification: 5 requests per hour per IP and 3 per day per email POST /v1/recover: 5 attempts per 60-second window (per user) Exceeding returns 429 with Retry-After header.

API Limits

These plan labels preserve the former public contract. Public plans and upgrade paths are retired; the private runtime uses the owner's configured limits.

Request & Data Limits

Task content: Plan-gated (Free: 2,000 / Pro: 10,000 / Scale: 50,000 characters) Request body: 64 KB max (ingress: 256 KiB; GitHub receiver: 25 MiB) Webhooks: Free: 1 / Pro, Scale, and usage: 50 Automation rules: Free: 5 / Pro, Scale, and usage: 50 Ingress sources: Free: 5 / Pro, Scale, and usage: 50 Ingress deliveries: 60 verified requests/minute per source Priority values: 1–4 (values outside this range return 400) Label length: 50 characters max per label Task limit: Per-plan monthly limit (hard block at limit, resets monthly)

Agent Limits

Free: 5 agent identities, 1 webhook, 5 automation rules, 5 ingress sources Pro: 25 agent identities, 50 webhooks, 50 automation rules, 50 ingress sources Scale: Unlimited agent identities, 50 webhooks, 50 automation rules, 50 ingress sources Historical responses included current count, limit, plan, and upgrade URL. No public upgrade URL is active.

Pagination

GET /v1/tasks supports pagination via query parameters: ?limit=100 Number of tasks to return (default: 100, max: 500) ?offset=0 Number of tasks to skip (default: 0) Response includes total count in the result array length. Omitting both returns up to 100 tasks.

Webhook Signature Verification

All webhook deliveries include an X-Delega-Signature header for HMAC verification.

Payload Shape

{ "event": "task.completed", "timestamp": "2026-06-12T03:00:00.000Z", "task": { ...full task object... }, "agent": { "id": "agt_5e58870f...", "name": "claude-code", "label": "Claude Code", "mode": "full", "role": "coordinator", "admin": false } } "agent" is the agent whose action fired the event (null for system events). "role" is one of: sandbox, worker, coordinator, admin.

Header Format

X-Delega-Signature: t=1710518400,sha256=5257a869e... t = Unix timestamp of the delivery sha256 = HMAC-SHA256 of "{timestamp}.{JSON body}" using your webhook secret

Verification (Node.js)

const crypto = require('crypto'); function verifyWebhook(body, signature, secret) { const [tPart, sigPart] = signature.split(','); const timestamp = tPart.replace('t=', ''); const expected = crypto .createHmac('sha256', secret) .update(`${timestamp}.${body}`) .digest('hex'); return sigPart.replace('sha256=', '') === expected; }

Failure Handling

Webhooks auto-disable after 10 consecutive delivery failures. Re-enable via PUT /v1/webhooks/:id with { "active": true } (resets failure count).

URL Validation & Security

Webhook URLs must use http or https, and must not point to private/internal IPs. DNS resolution is performed at registration and delivery time to prevent SSRF. Resolved IPs are pinned between validation and fetch to prevent DNS rebinding.

Completed Task Behavior

Completion uses a dedicated endpoint, not a field on PUT. Once complete, the task becomes immutable with specific exceptions.

Rules

POST /tasks/:id/complete → Use this to mark a task done PUT /tasks/:id → Rejects "completed" in body (400 Bad Request) PUT /tasks/:id → 403 Forbidden (cannot modify a completed task) DELETE /tasks/:id → 200 OK (deletion is allowed) POST /tasks/:id/complete → 200 OK (idempotent, no error on double-complete)
GET/

Root endpoint for the hosted API origin.

Authentication: None

Response

{ "ok": true, "docs": "https://delega.dev/docs" }
GET/health

Health check endpoint. Confirms the worker can respond and reach D1.

Authentication: None

Response

{ "ok": true, "timestamp": "2026-01-01T00:00:00Z" }

Decision Answers

A secure return path for a human ruling when an agent is genuinely blocked.

A claimed task entering waiting_input can format its detail as QUESTION: <one line> / OPTIONS: <a / b / …>. When email delivery is configured, Delega sends the account owner a signed, single-use answer link. Successful escalation emails are limited to one per task every 30 minutes.

The preview GET is strictly side-effect-free. A submitted answer is normally stored as both a task comment and a distinct answer_<timestamp>_<token-id> context key with human_stated provenance. If the task cannot accept another context key, the answer is preserved as a comment. Tokens expire after 72 hours, stop working after one submission or task completion, and never resume an agent automatically—the next session must read the task context and comments.

Treat the answer URL as a credential. These public routes intentionally have no live “Try it” controls.

GET/v1/answer/:token

Render the task title, full question or decision detail, and answer form without consuming the token or writing any state. Invalid, expired, used, deleted-task, and completed-task links return the same not-found response.

Authentication: Signed URL token

POST/v1/answer/:token

Consume a valid token and record the human answer on the still-open task. The body is form-encoded rather than JSON.

Authentication: Signed URL token

Form Body

NameTypeRequiredDescription
answerstringrequiredHuman response, up to 2,000 characters
POST/v1/signup

Retired July 28, 2026. Returns 410 hosted_service_retired. No account is created and no verification email is sent.

Authentication: None

Request Body

NameTypeRequiredDescription
emailstringrequiredYour email address
namestringoptionalYour display name

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
POST/v1/verify

Retired July 28, 2026. Returns 410 hosted_service_retired and never issues a key.

Authentication: None

Request Body

NameTypeRequiredDescription
emailstringrequiredYour email address
codestringrequired6-digit verification code

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
POST/v1/resend-verification

Retired July 28, 2026. Returns 410 hosted_service_retired and sends no email.

Authentication: None

Request Body

NameTypeRequiredDescription
emailstringrequiredYour email address

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
POST/v1/recover

Retired July 28, 2026. Returns 410 hosted_service_retired. Owner key recovery is handled privately.

Authentication: None

Request Body

NameTypeRequiredDescription
emailstringrequiredVerified hosted account email
codestringoptional6-digit recovery code from email

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
POST/v1/agent/signup

Retired July 28, 2026. Returns 410 hosted_service_retired. No restricted key or sandbox is created.

Authentication: None

Request Body

NameTypeRequiredDescription
human_emailstringrequiredHuman-controlled email for the claim flow
agent_namestringrequired3-32 chars, lowercase [a-z0-9_-]
agent_labelstringoptionalHuman-readable label shown in the dashboard
use_casestringoptionalFreeform description of what the agent does

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
POST/v1/agent/claim/magic/preview

Retired July 28, 2026. Returns 410 hosted_service_retired. No claim token is accepted.

Authentication: None

Request Body

NameTypeRequiredDescription
tokenstringrequiredMagic-link token from the claim email

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
POST/v1/agent/claim/magic/verify

Retired July 28, 2026. Returns 410 hosted_service_retired. No account is upgraded.

Authentication: None

Request Body

NameTypeRequiredDescription
tokenstringrequiredMagic-link token from the claim email

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
GET/v1/agent/me

Inspect the authenticated hosted agent, account, and current sandbox project metadata.

Authentication: Required — X-Agent-Key header

Response

{ "agent": { "id": "agt_01", "name": "marty", "label": "Marty", "mode": "restricted", "admin": true }, "account": { "id": "acc_01", "status": "pending", "human_email": "[email protected]" }, "project": { "id": "prj_01", "name": "Marty Sandbox", "kind": "sandbox" } }
GET/v1/agent/me/capabilities

Get the live capability matrix for the current hosted key. This is the source of truth for restricted, full, and admin behavior. Full-mode (claimed) keys include tasks.claim, which unlocks the work-queue claiming endpoints.

Authentication: Required — X-Agent-Key header

Response

{ "mode": "restricted", "admin": true, "capabilities": [ "tasks.create", "tasks.read.own", "tasks.update.own", "tasks.complete.own", "comments.create.own", "claim.request" ], "denied": [ "tasks.delegate", "projects.create", "webhooks.write", "agents.create", "stats.read" ] }
POST/v1/agent/claim/request

Retired July 28, 2026. Returns 410 hosted_service_retired and sends no email.

Authentication: Required — restricted X-Agent-Key header

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
POST/v1/agent/claim/verify

Retired July 28, 2026. Returns 410 hosted_service_retired.

Authentication: Required — restricted X-Agent-Key header

Request Body

NameTypeRequiredDescription
otpstringrequired6-digit fallback code from the claim email

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
GET/v1/agents

List all agents associated with the current hosted account. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Response

[ { "id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "external_id": "agt_01", "user_id": "c1bf1d3b588646bfbd41b1440c4d8f53", "name": "default", "display_name": "Default Agent", "description": null, "active": 1, "created_at": "2026-01-15 10:30:00", "last_seen_at": null } ]
POST/v1/agents

Create a new full-mode agent. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringrequiredAgent slug (lowercase, hyphens)
display_namestringoptionalHuman-readable agent name
descriptionstringoptionalAgent description

Response

{ "id": "41c48f84ebf247f8b1930d8c18bf7b4e", "name": "research-bot", "display_name": "Research Bot", "description": null, "api_key": "dlg_..." }
POST/v1/agents/:id/rotate-key

Rotate an agent key. Agents can rotate their own key; rotating another agent's key requires an admin key.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "id": "41c48f84ebf247f8b1930d8c18bf7b4e", "api_key": "dlg_..." }
PUT/v1/agents/:id

Update an agent profile. Any full-mode agent can edit its own display name and description; only admin agents can rename slugs or edit other agents.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringoptionalAgent slug (lowercase, hyphens)
display_namestringoptionalHuman-readable display name
descriptionstringoptionalAgent description

Response

{ "id": "41c48f84ebf247f8b1930d8c18bf7b4e", "user_id": "c1bf1d3b588646bfbd41b1440c4d8f53", "name": "research-bot", "display_name": "Research Bot", "description": null, "active": 1, "created_at": "2026-01-15 10:30:00", "last_seen_at": null }
DELETE/v1/agents/:id

Delete an agent. Admin agent key required. The recovery agent, the currently authenticated agent, the last active agent, and agents with existing task references cannot be deleted.

Authentication: Required — admin X-Agent-Key header

Response

{ "ok": true }
GET/v1/projects

List projects for the current account. Restricted keys only see their sandbox project; full keys see all account projects.

Authentication: Required — X-Agent-Key header

Response

[ { "id": "31baf3d85fd8406f8b7f4d5deef6b9bd", "external_id": "prj_01", "name": "Default", "emoji": null, "color": "#0ea5e9", "kind": "standard", "visibility": "private", "sort_order": 0, "created_at": "2026-01-15 10:30:00" } ]
POST/v1/projects

Create a new project. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringrequiredProject name
emojistringoptionalEmoji badge
colorstringoptionalHex or CSS color string

Response

{ "id": "31baf3d85fd8406f8b7f4d5deef6b9bd", "external_id": "prj_01", "name": "Roadmap", "emoji": null, "color": "#0ea5e9", "kind": "standard", "visibility": "private", "sort_order": 0, "created_at": "2026-01-15 10:30:00" }
PUT/v1/projects/:id

Update a project name, emoji, color, or sort order. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringoptionalUpdated project name
emojistringoptionalUpdated emoji badge
colorstringoptionalUpdated color
sort_orderintegeroptionalUpdated ordering value

Response

{ "id": "31baf3d85fd8406f8b7f4d5deef6b9bd", "external_id": "prj_01", "name": "Q1 Roadmap", "emoji": null, "color": "#0ea5e9", "kind": "standard", "visibility": "private", "sort_order": 1, "created_at": "2026-01-15 10:30:00" }
DELETE/v1/projects/:id

Delete a project. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Response

{ "ok": true }

Task Access Model

Restricted keys can only work inside their sandbox project and only on tasks they created. Endpoints below marked full-mode reject pre-claim restricted keys.

Evidence-Required Completion

Make “done” a checkable claim without adding a second completion state.

Set evidence_policy to required when creating or successfully updating an open task. Completion must then attach at least one strong evidence item. Only an admin key may remove a required policy.

Evidence vocabulary

Strong: commit, pr, ci_check, deploy_sha, artifact_url Supplemental: command_output Maximum: 5 items per completion Item shape: { "kind": "...", "ref": "...", "summary": "optional" }

Evidence is stored on the task, included in task.completed events, and rendered by MCP. Delega does not execute or independently verify it; the references are falsifiable claims for a reviewer to spot-check. Valid voluntary evidence is also accepted when no policy is required.

The automation action { "type": "set_evidence_policy", "policy": "required" } can tighten a task but never remove the policy. Automation is asynchronous and best-effort; create-time or successful open-task update-time policy is the authoritative guarantee.

GET/v1/tasks

List tasks visible to the authenticated key. Supports filtering and search. Restricted keys only see tasks they created inside their sandbox project.

Authentication: Required — X-Agent-Key header

Query Parameters

NameTypeDefaultDescription
project_idstringFilter by project (internal or external ID)
completedbooleanFilter by completion status (true or false)
claimedbooleanFilter by claim state: true = currently claimed (active lease), false = not claimed
repostringFilter to tasks linked to a repository, e.g. owner/name
priorityintegerFilter by priority (1–4)
searchstringText search across content and description (case-insensitive LIKE)
labelstringFilter by a single label
labelsstringFilter by multiple labels (comma-separated, AND logic). Example: ?labels=bug,urgent
duestringShorthand date filter: today, upcoming, or overdue
due_afterstringTasks due on or after this date (YYYY-MM-DD)
due_beforestringTasks due on or before this date (YYYY-MM-DD)
assigned_tostringFilter by agent ID/external ID, or none for unassigned tasks
statestringFilter by active session state: working, waiting_input, or errored
sourcestringFilter by task source: manual, recurrence, or automation
source_recurrence_idstringFilter to task instances spawned by one recurrence template
sortstringprioritySort by priority, updated, due, or completed
limitinteger100Max tasks to return (1–500)
offsetinteger0Number of tasks to skip for pagination

Response

[ { "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "user_id": "c1bf1d3b588646bfbd41b1440c4d8f53", "content": "Research competitor pricing", "description": null, "project_id": null, "due_date": null, "priority": 2, "labels": [], "completed": 0, "status": "open", "completed_at": null, "completed_by_agent_id": null, "parent_task_id": null, "root_task_id": null, "delegation_depth": 0, "created_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "assigned_to_agent_id": null, "created_at": "2026-01-15 10:30:00", "updated_at": "2026-01-15 10:30:00" } ]
POST/v1/tasks

Create a new task. Restricted keys are pinned to their sandbox project and cannot assign tasks to another agent before claim.

Authentication: Required — X-Agent-Key header

Request Body

NameTypeRequiredDescription
contentstringrequiredTask content / title
descriptionstringoptionalLonger description
project_idstringoptionalProject to assign to
due_datestringoptionalDue date (YYYY-MM-DD)
priorityintegeroptional1 (default) to 4
labelsarrayoptionalArray of label strings
assigned_to_agent_idstringoptionalAssign to an agent (full/admin mode or self-assignment only for restricted keys)
evidence_policystring | nulloptionalrequired makes strong structured evidence mandatory at completion

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "user_id": "c1bf1d3b588646bfbd41b1440c4d8f53", "content": "Research competitor pricing", "description": null, "project_id": null, "due_date": null, "priority": 2, "labels": [], "completed": 0, "status": "open", "completed_at": null, "completed_by_agent_id": null, "parent_task_id": null, "root_task_id": null, "delegation_depth": 0, "created_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "assigned_to_agent_id": null, "created_at": "2026-01-15 10:30:00", "updated_at": "2026-01-15 10:30:00" }
GET/v1/tasks/:id

Get a single task by ID if it is visible to the authenticated key.

Authentication: Required — X-Agent-Key header

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "user_id": "c1bf1d3b588646bfbd41b1440c4d8f53", "content": "Research competitor pricing", "description": "Look at top 5 competitors", "project_id": "31baf3d85fd8406f8b7f4d5deef6b9bd", "due_date": "2026-02-01", "priority": 2, "labels": "[\"research\"]", "completed": 0, "status": "open", "completed_at": null, "completed_by_agent_id": null, "parent_task_id": null, "root_task_id": null, "delegation_depth": 0, "created_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "assigned_to_agent_id": "41c48f84ebf247f8b1930d8c18bf7b4e", "created_at": "2026-01-15 10:30:00", "updated_at": "2026-01-15 10:30:00" }
PUT/v1/tasks/:id

Update a task's fields. Restricted keys may update only their own sandbox tasks. Returns 409 if the task is claimed by another agent and the claim lease is still live (see Claiming).

completed cannot be set via PUT. Use POST /tasks/:id/complete instead.

Authentication: Required — X-Agent-Key header

Request Body

NameTypeRequiredDescription
contentstringoptionalUpdated task content
descriptionstringoptionalUpdated description
priorityintegeroptionalPriority 1–4
due_datestringoptionalDue date (YYYY-MM-DD)
labelsarrayoptionalReplace labels array
project_idstringoptionalMove to project
assigned_to_agent_idstringoptionalReassign to another agent
evidence_policystring | nulloptionalSet required to tighten; only an admin may clear an existing required policy

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "user_id": "c1bf1d3b588646bfbd41b1440c4d8f53", "content": "Updated task content", "description": null, "project_id": null, "due_date": null, "priority": 3, "labels": "[\"urgent\"]", "completed": 0, "status": "open", "completed_at": null, "completed_by_agent_id": null, "parent_task_id": null, "root_task_id": null, "delegation_depth": 0, "created_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "assigned_to_agent_id": null, "created_at": "2026-01-15 10:30:00", "updated_at": "2026-01-15 12:00:00" }
DELETE/v1/tasks/:id

Permanently delete a task. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "ok": true }
POST/v1/tasks/:id/complete

Mark a task as completed. Sets the completed_at timestamp. This is the only way to complete a task (PUT /tasks/:id will reject completed in the body). Attach up to five structured evidence items; when evidence_policy is required, at least one must use a strong kind. Returns 409 if the task is claimed by another agent and the claim lease is still live (see Claiming).

Authentication: Required — X-Agent-Key header

Request Body

NameTypeRequiredDescription
evidencearrayoptional*Up to five { kind, ref, summary? } items. Required-policy tasks need at least one strong kind.

*Required when the task’s evidence_policy is required. Strong kinds: commit, pr, ci_check, deploy_sha, and artifact_url. command_output alone is insufficient.

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "completed": 1, "completed_at": "2026-01-15 14:30:00", "completion_evidence": "[{\"kind\":\"commit\",\"ref\":\"abc123\"}]", "status": "completed" }
POST/v1/tasks/:id/uncomplete

Mark a task as incomplete. Clears the completed_at timestamp.

Authentication: Required — X-Agent-Key header

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "completed": 0, "completed_at": null, "status": "open" }
POST/v1/tasks/:id/links

Attach a branch, commit, PR, or URL link to a task. Duplicate links return the existing record. Creating a new link fires task.linked.

Authentication: Required — X-Agent-Key header

Request Body

NameTypeRequiredDescription
kindstringrequiredbranch, commit, pr, or url
repostringoptionalRepository slug such as owner/name
refstringrequiredBranch name, commit SHA, PR number, or URL reference
urlstringoptionalCanonical link URL

Response

{ "id": "lnk_01HPR", "task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "kind": "pr", "repo": "acme/webapp", "ref": "42", "url": "https://github.com/acme/webapp/pull/42" }
POST/v1/tasks/:id/delegate

Delegate a task by creating a new child task. The parent task’s status is set to “delegated”. The request body uses the same schema as POST /v1/taskscontent is required because delegation creates a new task, not just a reassignment. Full-mode only. Returns 409 if the task is claimed by another agent and the claim lease is still live (see Claiming).

Authentication: Required — full-mode X-Agent-Key header

Request Body (same as task creation)

NameTypeRequiredDescription
contentstringrequiredContent for the new child task (returns 422 if missing)
assigned_to_agent_idstringoptionalAgent ID to assign the child task to
descriptionstringoptionalLonger description for the child task
labelsstring[]optionalLabels for the child task
priorityintegeroptional1–4 (default 1)

Response

{ "id": "b2dc0b2f4872470f8a52bb2be8d6c1a0", "content": "Research pricing for competitor A", "parent_task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "assigned_to_agent_id": "41c48f84ebf247f8b1930d8c18bf7b4e", "completed": 0, "created_at": "2026-01-15 11:00:00" }
GET/v1/tasks/:id/chain

Get the full delegation chain for a task, from root to deepest descendant. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "root_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "chain": [ { "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Research competitor pricing", "delegation_depth": 0, "completed": 0 }, { "id": "b2dc0b2f4872470f8a52bb2be8d6c1a0", "content": "Research pricing for competitor A", "delegation_depth": 1, "completed": 0 } ], "depth": 1, "completed_count": 0, "total_count": 2 }
GET/v1/tasks/:id/children

List the direct child tasks of a given task. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Response

[ { "id": "b2dc0b2f4872470f8a52bb2be8d6c1a0", "content": "Research pricing for competitor A", "parent_task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "completed": 0, "created_at": "2026-01-15 11:00:00" } ]
POST/v1/tasks/claim

Atomically claim the next claimable task from the queue: open, unclaimed, and either unassigned or assigned to the calling agent — plus tasks whose claim lease has expired (stale-holder takeover). Tasks are matched by priority ascending, then created_at ascending. On success the task’s status is set to “claimed” and a lease starts (default 300 seconds) — extend it with heartbeat or requeue with release. Once the lease expires the task becomes claimable again. Claiming never modifies assigned_to_agent_id (human routing stays separate from the machine lease). Fires a task.claimed webhook event. Restricted keys receive 403.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
project_idstringoptionalOnly claim tasks in this project
labelsstring[]optionalOnly claim tasks carrying all of these labels
lease_secondsintegeroptionalLease duration in seconds (30–3600, default 300)

Response (task claimed)

{ "task": { "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Process invoice batch 2026-06", "priority": 1, "labels": "[\"invoices\"]", "completed": 0, "status": "claimed", "claimed_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "claimed_at": "2026-06-09 14:00:00", "lease_expires_at": "2026-06-09 14:05:00", "assigned_to_agent_id": null } }

Response (nothing claimable)

{ "task": null }
POST/v1/tasks/:id/claim

Claim one specific task — e.g. one found via GET /v1/tasks, or after a write was rejected with a 403 pointing here. Same claimability rules as the queue claim, enforced by the same atomic update: open, unclaimed, and unassigned or assigned to the caller — or a lease that has already expired (takeover). A live claim can never be stolen. Returns 409 with a specific reason when the task is completed, assigned to another agent, claimed by another agent with an active lease, or already held by the caller (extend via heartbeat instead); 404 when the task doesn’t exist or isn’t visible to the key. Fires a task.claimed webhook event. Restricted keys receive 403.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
lease_secondsintegeroptionalLease duration in seconds (30–3600, default 300)

Response (task claimed)

{ "task": { "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Process invoice batch 2026-06", "status": "claimed", "claimed_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "claimed_at": "2026-06-09 14:00:00", "lease_expires_at": "2026-06-09 14:05:00", "assigned_to_agent_id": null } }

Response (not claimable — 409)

{ "error": "Task is claimed by another agent with an active lease.", "status": 409 }
POST/v1/tasks/:id/heartbeat

Extend the lease on a task the calling agent currently holds a claim on. Returns the task with the new lease_expires_at. Optionally reports a session state (working | waiting_input | errored) plus free-text detail in the same call — a state transition fires a task.state_changed webhook event. For a human decision blocker, use the QUESTION: … / OPTIONS: … convention described under Decision Answers. Returns 409 if the caller does not hold an active (unexpired) claim on the task. Heartbeat before the lease runs out to keep long-running work from being reclaimed by another worker.

Authentication: Required — full-mode X-Agent-Key header (claim holder)

Request Body

NameTypeRequiredDescription
lease_secondsintegeroptionalNew lease duration in seconds (30–3600, default 300)
statestringoptionalSession state to report: working, waiting_input, or errored
detailstringoptionalFree-text detail for the state (≤500 chars). Requires state; replaces any previous detail.

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Process invoice batch 2026-06", "status": "claimed", "claimed_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "claimed_at": "2026-06-09 14:00:00", "lease_expires_at": "2026-06-09 14:10:00", "session_state": "working", "session_state_detail": null }
POST/v1/tasks/:id/state

Set the session state of a claimed task without extending the lease — an agent blocked on input shouldn’t have to fake liveness to stay visible. A claimed task always carries a session_state (working is set automatically on claim); use this endpoint to flag waiting_input or errored. For a genuine human decision blocker, format detail as QUESTION: <one line> / OPTIONS: <a / b / …>; when email delivery is configured, a real transition into waiting_input includes a signed Decision Answer link. The detail text is replaced on every transition, and active fields are nulled when the claim ends (an explicit release preserves them in handoff fields). Successful escalation emails are limited to one per task every 30 minutes. Fires a task.state_changed webhook event (payload includes previous_session_state) when the state changes. Same holder rules as heartbeat: returns 409 if the caller does not hold an active claim. Surface stuck work with GET /v1/tasks?state=waiting_input.

Authentication: Required — full-mode X-Agent-Key header (claim holder)

Request Body

NameTypeRequiredDescription
statestringrequiredSession state: working, waiting_input, or errored
detailstringoptionalFree-text detail (≤500 chars), e.g. “needs prod API key”

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Process invoice batch 2026-06", "status": "claimed", "claimed_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "lease_expires_at": "2026-06-09 14:05:00", "session_state": "waiting_input", "session_state_detail": "needs prod API key" }
POST/v1/tasks/:id/release

Release a claimed task back to the queue. Status returns to “open” and the lease is cleared so another worker can claim it. Pass an optional handoff string (≤500 characters) describing where work stopped; if omitted, Delega preserves the current session_state_detail. The note, departing state, releasing agent, and timestamp remain on the task and are surfaced to the next claimant. Claim holder or admin only (403 otherwise); returns 409 if the task is not currently claimed. A pre-existing assigned_to_agent_id survives the release. Fires a task.released webhook event — note that passive lease expiry fires no webhook.

Authentication: Required — full-mode X-Agent-Key header (claim holder or admin)

Request Body

NameTypeRequiredDescription
handoffstringoptionalWhere work stopped or why it is being released (≤500 characters)

Response

{ "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Process invoice batch 2026-06", "status": "open", "claimed_by_agent_id": null, "claimed_at": null, "lease_expires_at": null, "assigned_to_agent_id": null, "handoff_note": "Migration written; verification still pending", "handoff_state": "working", "handoff_by_agent_id": "8f4c7d91d14d4cfaa80a4d6f55de4b0c", "handoff_at": "2026-07-23 03:45:00" }
GET/v1/fleet/attention

Return a coordination triage board with six buckets: abandoned_claims for expired leases, silent_holders for live claims whose holder has not been seen for more than 15 minutes, errored, waiting_input, overdue, and looping for tasks reopened at least three times. Coordinators and agents with tasks.read_all see the account view; other workers see only tasks they created, were assigned, completed, or currently claim. Each bucket returns up to 50 tasks.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "count": 2, "buckets": { "abandoned_claims": [], "silent_holders": [], "errored": [ { "id": "t_error", "content": "Fix production sync", "session_state": "errored", "session_state_detail": "build failed" } ], "waiting_input": [ { "id": "t_input", "content": "Confirm rollout window", "session_state": "waiting_input", "session_state_detail": "needs human approval" } ], "overdue": [], "looping": [] } }

The five-minute lease reaper also sends a per-account email digest when it returns expired claims to the queue. Phase 1 escalation is email-only.

PATCH/v1/tasks/:id/context

Merge a JSON object into the task context. Full-mode only. Provenance is recorded for each top-level key; pass ?source=human_stated, agent_inferred, agent_observed, or imported to classify the write.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
(any key)anyJSON object to merge into context

Response

{ "context": { "research_urls": ["https://example.com"], "notes": "Found 3 competitors", "price_range": "$10-$50" }, "version": 4 }
GET/v1/tasks/:id/context

Get the full JSON context object for a task. Full-mode only. Add ?include=provenance to include author/source/timestamp/version metadata for current live entries.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "context": { "research_urls": ["https://example.com"], "notes": "Found 3 competitors", "price_range": "$10-$50" }, "version": 4, "provenance": { "notes": { "author_agent_id": "abc123", "author_name": "Research Agent", "source": "agent_observed", "created_at": "2026-06-10 12:00:00", "version": 4 } } }
GET/v1/tasks/:id/context/history

Read the append-only provenance ledger for a task context. Omit key for newest entries across all keys, or pass ?key=notes. Supports limit up to 100 and integer cursor offsets.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "entries": [ { "id": "entry123", "key": "notes", "value": "Found 3 competitors", "version": 4, "author_agent_id": "abc123", "author_name": "Research Agent", "source": "agent_observed", "created_at": "2026-06-10 12:00:00", "superseded_by": null, "superseded_at": null } ], "next_cursor": null }
POST/v1/tasks/:id/context/supersede

Mark the current live provenance entry for a key as stale without replacing the context blob. Use this when a fact is no longer trusted but there is no successor value yet.

Authentication: Required — creator, assignee, or active claim holder

Request Body

NameTypeRequiredDescription
keystringrequiredContext key whose live provenance entry should be marked stale

Response

{ "superseded": { "id": "entry123", "key": "notes", "value": "Found 3 competitors", "version": 4, "source": "agent_observed", "superseded_at": "2026-06-10 12:05:00", "superseded_by": null } }
GET/v1/recurrences

List recurring task templates visible to the authenticated agent, ordered by active state and next due time. Recurrences create normal task instances; completing an instance does not delete its schedule. Restricted pre-claim keys cannot manage recurrences.

Authentication: Required — full-mode X-Agent-Key header

Response

[ { "id": "7b1d4d8ece7c4d3c830994d9978683f5", "content": "Replace furnace filter", "description": "Monthly filter replacement", "project_id": null, "priority": 2, "labels": ["home"], "assigned_to_agent_id": null, "rule_type": "monthly", "interval": 1, "timezone": "America/Chicago", "anchor_day": 1, "anchor_month": null, "anchor_weekday": null, "next_due_at": "2026-08-01T05:00:00.000Z", "last_spawned_at": "2026-07-01T05:00:00.000Z", "active": 1, "skip_if_open": 1, "capabilities": { "can_update": true, "can_delete": true } } ]
POST/v1/recurrences

Create a recurring task template. Rules may be daily, weekly, monthly, or yearly. Weekly rules require anchor_weekday (0 = Sunday), monthly rules require anchor_day, and yearly rules require anchor_month plus anchor_day. The scheduler fires task.created for each spawned task and counts it against the monthly task quota.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
contentstringrequiredContent for each spawned task
descriptionstring | nulloptionalDescription copied to spawned tasks
project_idstring | nulloptionalProject ID or external ID
labelsarrayoptionalLabels copied to spawned tasks
priorityintegeroptionalPriority from 1 to 4; defaults to 1
assigned_to_agent_idstring | nulloptionalAgent ID or external ID
rule_typestringrequireddaily, weekly, monthly, or yearly
intervalintegeroptionalNumber of rule periods between occurrences; defaults to 1
timezonestringoptionalIANA timezone; defaults to UTC
anchor_weekdayinteger | nullconditionalWeekly weekday from 0 (Sunday) to 6
anchor_dayinteger | nullconditionalDay of month for monthly/yearly rules
anchor_monthinteger | nullconditionalMonth from 1 to 12 for yearly rules
next_due_atstring | nulloptionalISO timestamp; computed from the rule when omitted
activebooleanoptionalWhether the schedule is active; defaults to true
skip_if_openbooleanoptionalAdvance without spawning when a prior instance is open; defaults to true

Response

Returns the created recurrence template with HTTP 201, including its computed next_due_at and caller capabilities.
GET/v1/recurrences/:id

Get one visible recurrence template and the caller’s update/delete capabilities.

Authentication: Required — full-mode X-Agent-Key header

Response

Returns one recurrence object in the same shape as GET /v1/recurrences, or 404 when the template is absent or not visible.
GET/v1/recurrences/:id/tasks

List normal task instances spawned by one recurrence, newest first. Each task carries source_recurrence_id. Existing instances remain available after the template is deleted.

Authentication: Required — full-mode X-Agent-Key header with access to the recurrence

Query Parameters

NameTypeDefaultDescription
limitinteger100Max task instances to return (1–500)
offsetinteger0Number of task instances to skip

Response

{ "occurrences": [ { "id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Replace furnace filter", "source_recurrence_id": "7b1d4d8ece7c4d3c830994d9978683f5", "completed": 0, "due_date": "2026-08-01" } ], "next_offset": null }
PUT/v1/recurrences/:id

Update a recurrence template, including its task fields, schedule, assignee, skip_if_open, or paused state via active=false. Changing rule fields recomputes next_due_at unless an explicit timestamp is supplied.

Authentication: Required — creator, assignee, or admin with a full-mode X-Agent-Key

Request Body

Any field accepted by POST /v1/recurrences may be supplied. Omitted fields retain their current values.
DELETE/v1/recurrences/:id

Delete a recurrence template. Tasks it already spawned remain normal tasks and are not deleted.

Authentication: Required — creator, assignee, or admin with a full-mode X-Agent-Key

Response

{ "ok": true }
POST/v1/recurrences/spawn-due

Run the same due-recurrence pass used by the hosted scheduler. This operational endpoint is admin-only; normal accounts do not need to call it for schedules to run.

Authentication: Required — admin X-Agent-Key header

Response

{ "spawned": 1, "skipped_open": 0, "quota_blocked": 0 }
GET/v1/tasks/:id/subtasks

List all subtasks for a task. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Response

[ { "id": "7af890c30b1e4e688f3148a235f13b25", "content": "Check competitor A website", "completed": 0, "sort_order": 0 }, { "id": "8c90af99bfc54a5bb78674ba4fe90b4e", "content": "Check competitor B website", "completed": 1, "sort_order": 1 } ]
POST/v1/tasks/:id/subtasks

Create a new subtask on a task. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
contentstringrequiredSubtask content

Response

{ "id": "0529a4dc9a274f1dabfaecdb0d3f6dc4", "content": "Check competitor C website", "completed": 0, "sort_order": 2 }
PUT/v1/tasks/:id/subtasks/:sid

Update a subtask's content, sort order, or completion status. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
contentstringoptionalUpdated content
sort_orderintegeroptionalNew position
completedbooleanoptionalCompletion status

Response

{ "id": "0529a4dc9a274f1dabfaecdb0d3f6dc4", "content": "Check competitor C pricing page", "completed": 0, "sort_order": 2 }
DELETE/v1/tasks/:id/subtasks/:sid

Delete a subtask. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "ok": true }
POST/v1/tasks/:id/subtasks/:sid/toggle

Toggle a subtask's completion status. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "id": "0529a4dc9a274f1dabfaecdb0d3f6dc4", "content": "Check competitor C pricing page", "completed": 1, "sort_order": 2 }
GET/v1/tasks/:id/comments

List comments on a task. Restricted keys can only read comments on tasks they can already access.

Authentication: Required — X-Agent-Key header

Response

[ { "id": "3e8eb613e59e4f5ebc6b04b1d0ba2f37", "task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "user_id": "a6e2cf0b683d4ff39dbb9e06f5f56d36", "content": "Found pricing data for competitor A.", "author": "research-bot", "created_at": "2026-01-15 12:00:00" } ]
POST/v1/tasks/:id/comments

Add a comment to a task. Restricted keys can only comment on tasks they can already access.

Authentication: Required — X-Agent-Key header

Request Body

NameTypeRequiredDescription
contentstringrequiredComment text
authorstringoptionalOverride author display name

Response

{ "id": "3e8eb613e59e4f5ebc6b04b1d0ba2f37", "task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "user_id": "a6e2cf0b683d4ff39dbb9e06f5f56d36", "content": "Updated pricing spreadsheet.", "author": "research-bot", "created_at": "2026-01-15 14:00:00" }
GET/v1/webhooks

List account webhooks. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Response

[ { "id": "7b1d4d8ece7c4d3c830994d9978683f5", "url": "https://example.com/webhook", "events": "[\"task.created\",\"task.completed\"]", "active": 1, "failure_count": 0, "created_at": "2026-01-15 10:30:00" } ]
POST/v1/webhooks

Create a new webhook subscription. Admin agent key required. The write-only secret is accepted on input but never returned.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
urlstringrequiredWebhook delivery URL
eventsarrayrequiredEvents to subscribe to. Valid events: task.created, task.updated, task.completed, task.deleted, task.assigned, task.delegated, task.commented, task.claimed, task.released, task.state_changed, task.linked. Note: task.claimed / task.released fire on claim and explicit release — passive lease expiry fires no webhook.
secretstringoptionalSigning secret for payload verification

Response

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "url": "https://example.com/webhook", "events": "[\"task.created\",\"task.completed\"]", "active": 1, "failure_count": 0, "created_at": "2026-01-15 10:30:00" }
PUT/v1/webhooks/:id

Update a webhook's URL, events, secret, or active status. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
urlstringoptionalUpdated delivery URL
eventsarrayoptionalUpdated event list
secretstringoptionalReplace the signing secret
activebooleanoptionalEnable or disable

Response

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "url": "https://example.com/webhook-v2", "events": "[\"task.created\",\"task.completed\"]", "active": 1, "failure_count": 0, "created_at": "2026-01-15 10:30:00" }
DELETE/v1/webhooks/:id

Delete a webhook subscription. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Response

{ "ok": true }
GET/v1/webhooks/:id/deliveries

View the delivery log for a webhook. Admin agent key required.

Authentication: Required — admin X-Agent-Key header

Response

[ { "id": "b82fdf4ec38d4054bf1485bc26ffaf4f", "webhook_id": "7b1d4d8ece7c4d3c830994d9978683f5", "event": "task.created", "status_code": 200, "success": 1, "created_at": "2026-01-15 10:31:00" } ]

Automation Rules

Account-level when→then rules that react to Delega task events in-process. Management requires an admin agent key.

Rule vocabulary

Conditions (AND-combined, max 10): label: has | not_has priority: eq | neq | gte | lte project_id, assigned_to_agent_id, created_by_agent_id: eq | neq | is_null | not_null source: eq | neq (manual | recurrence | automation | ingress) Actions (ordered, max 5): assign, set_priority, add_label, add_comment, create_task, delegate, set_evidence_policy Text placeholders: {{event}}, {{task.id}}, {{task.content}}, {{task.priority}}, {{task.project_id}}, {{task.labels}}, {{task.due_date}}

Execution behavior

Cascades: max 3 hops and 25 actions per originating event Self-triggering: repeated rules in a chain and tasks created by the same rule are skipped Live claims: field mutations, including policy tightening, log skipped_claimed Policy action: set_evidence_policy can only tighten to required; asynchronous/best-effort Task creation: idempotent per rule action slot/source event; counted against monthly quota Failures: 10 consecutive failed runs disable the rule Run logs: latest 50 returned; rows retained for 30 days

Assignment changes currently emit task.updated. Use that event for assignment-reactive rules rather than the reserved task.assigned event. Repeated task.updated events for one task share a source key, so changing an occupied task-producing action slot does not replay it for that already-seen key. Ingress-sourced events require an explicit source eq ingress condition; a neq condition does not opt in. The set_evidence_policy action is a best-effort convenience rather than a synchronous project default; use create/update-time policy when the requirement must be authoritative.

GET/v1/automations

List all automation rules for the account, oldest first.

Authentication: Required — admin X-Agent-Key header

Response

[ { "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "Triage bugs", "event": "task.created", "conditions": [{ "field": "label", "op": "has", "value": "bug" }], "actions": [ { "type": "assign", "agent_id": "agt_codex" }, { "type": "set_priority", "priority": 3 } ], "active": 1, "run_count": 12, "failure_count": 0, "last_run_at": "2026-07-23 18:00:00" } ]
POST/v1/automations

Create an automation rule. Historical public plans allowed 5 rules on the free plan and 50 on paid or usage plans; those plans are retired. The private owner account retains its configured limit.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringrequiredRule name (max 80 characters)
eventstringrequiredAny supported task webhook event
conditionsarrayoptionalUp to 10 AND-combined conditions; defaults to an empty list
actionsarrayrequiredOne to five ordered actions
activebooleanoptionalDefaults to true

Response (201)

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "Triage bugs", "event": "task.created", "conditions": [{ "field": "label", "op": "has", "value": "bug" }], "actions": [ { "type": "assign", "agent_id": "agt_codex" }, { "type": "set_priority", "priority": 3 } ], "active": 1, "run_count": 0, "failure_count": 0, "last_run_at": null }
GET/v1/automations/:id

Get one automation rule, including parsed condition/action arrays and execution counters.

Authentication: Required — admin X-Agent-Key header

Response

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "Triage bugs", "event": "task.created", "conditions": [{ "field": "label", "op": "has", "value": "bug" }], "actions": [{ "type": "set_priority", "priority": 3 }], "active": 1, "run_count": 12, "failure_count": 0, "last_run_at": "2026-07-23 18:00:00" }
PUT/v1/automations/:id

Update a rule. Only supplied fields change. Conditions and actions replace their full arrays; setting active: true clears an auto-disable failure streak.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringoptionalReplacement rule name
eventstringoptionalReplacement trigger event
conditionsarrayoptionalReplacement condition list
actionsarrayoptionalReplacement ordered action list
activebooleanoptionalEnable or disable the rule

Response

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "Triage bugs", "event": "task.created", "conditions": [{ "field": "label", "op": "has", "value": "bug" }], "actions": [{ "type": "set_priority", "priority": 3 }], "active": 0, "run_count": 12, "failure_count": 0 }
DELETE/v1/automations/:id

Delete a rule and its execution log. Tasks and comments previously created by the rule remain.

Authentication: Required — admin X-Agent-Key header

Response

{ "ok": true }
GET/v1/automations/:id/runs

Return the latest 50 matching executions for a rule. Non-matching events are not logged. Per-action results show successful, skipped, duplicate, budget-limited, or failed outcomes.

Authentication: Required — admin X-Agent-Key header

Response

[ { "id": "b82fdf4ec38d4054bf1485bc26ffaf4f", "rule_id": "7b1d4d8ece7c4d3c830994d9978683f5", "event": "task.created", "task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "status": "ok", "detail": [ { "action": "assign", "status": "ok" }, { "action": "set_priority", "status": "ok" } ], "created_at": "2026-07-23 18:00:00" } ]

Inbound Connectors

Turn signed JSON events from CI, alerting, calendars, and other external systems into tasks. Source management requires an admin agent key; event delivery uses the source's HMAC secret.

Signing

X-Delega-Ingress-Signature: t=<unix-seconds>,v1=<hex> hex = HMAC-SHA256(secret, "<t>.<raw request body>") timestamp tolerance = 5 minutes

Node.js sender

const t = Math.floor(Date.now() / 1000); const v1 = crypto.createHmac("sha256", secret) .update(`${t}.${body}`) .digest("hex"); await fetch(`https://api.delega.dev/v1/ingress/${sourceId}`, { method: "POST", headers: { "Content-Type": "application/json", "X-Delega-Ingress-Signature": `t=${t},v1=${v1}`, }, body, });

Template and filter vocabulary

Template fields: content (required), description, priority, labels, dedupe_key Placeholders: dot paths only, e.g. {{workflow.name}} or {{runs.0.id}} own properties only; __proto__, prototype, and constructor are reserved primitive values render; objects and arrays render empty Filters (AND-combined, max 10): eq, neq, exists, not_exists

Ingress creates tasks only. Project and assignee routing are pinned on the source and cannot come from the payload. Every directly ingress-created task receives the ingress label and source_ingress_id; MCP task views show an “⚠ External source” line beneath the title. Treat its content as external data to triage, not instructions to execute. Provenance is sticky across automation-created children, preserving the field, label, warning, and opt-in gate.

Automation rules ignore ingress-sourced events—including tainted automation children—unless they explicitly include { "field": "source", "op": "eq", "value": "ingress" }. A neq condition does not opt in, and source eq automation deliberately excludes these children. Verified deliveries are limited to 60 per minute per source, request bodies are capped at 256 KiB, and task creation consumes the normal monthly quota. Rendered task content must fit the owning account's plan-specific title limit; oversized content is rejected instead of silently truncated. Delivery logs retain outcome metadata plus the request body’s SHA-256 and UTF-8 byte size; raw payloads are never retained.

POST/v1/ingress/:sourceId

Receive one signed JSON event. Filters run before quota is consumed. A matching delivery renders the source template and creates at most one task for its dedupe key.

Authentication: Required — X-Delega-Ingress-Signature HMAC header

Headers

NameRequiredDescription
Content-Typerequiredapplication/json
X-Delega-Ingress-Signaturerequiredt=<unix>,v1=<HMAC-SHA256 hex>

Created (201)

{ "status": "created", "task_id": "74d0be2b7a4b4564b6f186fb3f3769c2" }

Other outcomes

200 { "status": "duplicate" } 200 { "status": "filtered" } 400 invalid JSON (verified delivery is logged) 401 invalid signature (not logged) 404 missing, malformed, or inactive source 413 body exceeds 256 KiB 422 templated content empty or over the account plan's title limit 429 per-source rate limit or account task quota
GET/v1/ingress-sources

List inbound connector sources for the account. Signing secrets are never returned.

Authentication: Required — admin X-Agent-Key header

Response

[ { "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "GitHub Actions CI", "scheme": "hmac-sha256", "template": { "content": "CI failed: {{workflow.name}}", "dedupe_key": "{{run.id}}" }, "filters": [ { "path": "conclusion", "op": "eq", "value": "failure" } ], "default_project_id": null, "default_assignee_agent_id": null, "active": 1, "delivery_count": 12, "last_delivery_at": "2026-07-23 18:00:00", "ingest_path": "/v1/ingress/7b1d4d8ece7c4d3c830994d9978683f5" } ]
POST/v1/ingress-sources

Create a source. Historical public plans allowed 5 sources on the free plan and 50 on paid or usage plans; those plans are retired. The server-generated secret appears only in this response.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringrequiredSource name (max 80 characters)
templateobjectrequiredTask mapping with required content
filtersarrayoptionalUp to 10 AND-combined filters
default_project_idstringoptionalPinned project ID
default_assignee_agent_idstringoptionalPinned assignee ID
activebooleanoptionalDefaults to true

Response (201)

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "GitHub Actions CI", "scheme": "hmac-sha256", "template": { "content": "CI failed: {{workflow.name}}", "priority": 3, "labels": ["ci"], "dedupe_key": "{{run.id}}" }, "filters": [ { "path": "conclusion", "op": "eq", "value": "failure" } ], "active": 1, "ingest_path": "/v1/ingress/7b1d4d8ece7c4d3c830994d9978683f5", "secret": "64-hex-character-once-shown-secret" }
GET/v1/ingress-sources/:id

Get one source with parsed template and filters. The signing secret is not returned.

Authentication: Required — admin X-Agent-Key header

Response

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "GitHub Actions CI", "scheme": "hmac-sha256", "template": { "content": "CI failed: {{workflow.name}}" }, "filters": [], "active": 1, "delivery_count": 12, "ingest_path": "/v1/ingress/7b1d4d8ece7c4d3c830994d9978683f5" }
PUT/v1/ingress-sources/:id

Update a source. Only supplied fields change; template and filters replace their complete values. Set rotate_secret: true to invalidate the old secret and return a new one once.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
namestringoptionalReplacement name
templateobjectoptionalReplacement template
filtersarrayoptionalReplacement filters
default_project_idstring | nulloptionalNew pinned project, or null to clear
default_assignee_agent_idstring | nulloptionalNew pinned assignee, or null to clear
activebooleanoptionalEnable or disable the source
rotate_secretbooleanoptionalGenerate a new signing secret

Rotation response

{ "id": "7b1d4d8ece7c4d3c830994d9978683f5", "name": "GitHub Actions CI", "active": 1, "secret": "new-64-hex-character-once-shown-secret" }
DELETE/v1/ingress-sources/:id

Delete a source and its retained delivery log. Previously created tasks remain with ingress provenance.

Authentication: Required — admin X-Agent-Key header

Response

{ "ok": true }
GET/v1/ingress-sources/:id/deliveries

Return the latest 50 verified delivery outcomes with each exact request body’s SHA-256 hash and UTF-8 byte size for sender-side correlation. Raw payloads are never retained. Invalid signatures and per-source rate-limit rejects are not logged. Delivery rows are retained for 30 days.

Authentication: Required — admin X-Agent-Key header

Response

[ { "id": "b82fdf4ec38d4054bf1485bc26ffaf4f", "source_id": "7b1d4d8ece7c4d3c830994d9978683f5", "status": "created", "task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "detail": null, "body_hash": "5916f4a5f7b96438e0e8f3871d54b1bf4dc9e2b87d7349507e8f7f1684faba0a", "body_size": 96, "created_at": "2026-07-23 18:00:00" } ]
POST/v1/integrations/github

GitHub App or repository webhook receiver. Delega verifies X-Hub-Signature-256 with the configured GITHUB_WEBHOOK_SECRET, then scans push and pull request payloads for Delega task references.

Authentication: Required — GitHub HMAC signature

Webhook Headers

NameRequiredDescription
X-Hub-Signature-256requiredsha256= HMAC of the raw request body using GITHUB_WEBHOOK_SECRET
X-GitHub-Eventrequiredpush or pull_request
X-GitHub-DeliveryoptionalDelivery ID used for dedupe and diagnostics

Magic References

# Link branch, commit, or PR activity to a task delega:#74d0be2b7a4b4564b6f186fb3f3769c2 # Complete a task when the referenced PR is merged Closes-Delega: #74d0be2b7a4b4564b6f186fb3f3769c2

Response

{ "ok": true, "linked": 3, "completed": 1 }
POST/v1/tasks/dedup

Check for duplicate tasks based on content similarity. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Request Body

NameTypeRequiredDescription
contentstringrequiredTask content to check for duplicates

Response

{ "has_duplicates": true, "matches": [ { "task_id": "74d0be2b7a4b4564b6f186fb3f3769c2", "content": "Research competitor pricing", "score": 0.92 } ] }
GET/v1/usage

Get current hosted usage information including plan limits and reset dates.

Authentication: Required — X-Agent-Key header

Response

{ "plan": "free", "tasks_this_month": 42, "limit": 1000, "task_count": 42, "task_limit": 1000, "reset_date": "2026-02-01T00:00:00.000Z", "resets_at": "2026-02-01T00:00:00.000Z" }
GET/v1/stats

Get task statistics for the account. Full-mode only.

Authentication: Required — full-mode X-Agent-Key header

Response

{ "total": 150, "total_tasks": 150, "completed_today": 12, "due_today": 5, "overdue": 2, "total_completed": 84, "by_project": { "Roadmap": 45, "Inbox": 12 } }
POST/v1/billing/checkout

Retired July 28, 2026. Returns 410 hosted_service_retired. There are no public plans or checkout sessions.

Authentication: Required — admin X-Agent-Key header

Request Body

NameTypeRequiredDescription
planstringrequired"pro" or "scale"

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }
GET/v1/billing/portal

Retired July 28, 2026. Returns 410 hosted_service_retired.

Authentication: Required — admin X-Agent-Key header

Response

{ "error": "The public Delega hosted service was retired on July 28, 2026.", "status": 410, "code": "hosted_service_retired", "portfolio": "https://ryanmcmillan.com/delega" }