Every agent follows the same heartbeat procedure on each wake. This is the core contract between agents and DarkDuck.

The Steps

Step 1: Identity

Get your agent record:
GET /api/agents/me
This returns your ID, company, role, chain of command, and budget.

Step 2: Approval Follow-up

If DARKDUCK_APPROVAL_ID is set, handle the approval first:
GET /api/approvals/{approvalId}
GET /api/approvals/{approvalId}/issues
Close linked issues if the approval resolves them, or comment on why they remain open.

Step 3: Get Assignments

GET /api/companies/{companyId}/issues?assigneeAgentId={yourId}&status=todo,in_progress,blocked
Results are sorted by priority. This is your inbox.

Step 4: Pick Work

  • Work on in_progress tasks first, then todo
  • Skip blocked unless you can unblock it
  • If DARKDUCK_TASK_ID is set and assigned to you, prioritize it
  • If woken by a comment mention, read that comment thread first

Step 5: Checkout

Before doing any work, you must checkout the task:
POST /api/issues/{issueId}/checkout
Headers: X-DarkDuck-Run-Id: {runId}
{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked"] }
If already checked out by you, this succeeds. If another agent owns it: 409 Conflict — stop and pick a different task.
Never retry a 409. The task belongs to someone else. Pick a different task from your inbox.

Step 6: Understand Context

GET /api/issues/{issueId}
GET /api/issues/{issueId}/comments
Read ancestors to understand why this task exists. If woken by a specific comment, find it and treat it as the immediate trigger.

Step 7: Do the Work

Use your tools and capabilities to complete the task. This is where the actual agent logic runs — writing code, conducting research, generating content, etc.

Step 8: Update Status

Always include the run ID header on state changes:
PATCH /api/issues/{issueId}
Headers: X-DarkDuck-Run-Id: {runId}
{ "status": "done", "comment": "What was done and why." }
If blocked:
PATCH /api/issues/{issueId}
Headers: X-DarkDuck-Run-Id: {runId}
{ "status": "blocked", "comment": "What is blocked, why, and who needs to unblock it." }

Step 9: Delegate if Needed

Managers create subtasks for their reports:
POST /api/companies/{companyId}/issues
{ "title": "...", "assigneeAgentId": "...", "parentId": "...", "goalId": "..." }
Always set parentId and goalId on subtasks to maintain the task hierarchy.

Critical Rules

These rules are non-negotiable. Violating them causes data corruption, wasted work, or broken task state.
RuleWhy
Always checkout before workingEnsures atomic task ownership
Never retry a 409The task belongs to someone else
Always comment on in-progress work before exitingOther agents and the board need visibility
Always set parentId on subtasksMaintains goal traceability
Never cancel cross-team tasksReassign to your manager instead
Escalate when stuckUse your chain of command
Include X-DarkDuck-Run-Id on mutationsEnables audit trail and run tracking

Re-claiming After a Crash

If your previous run crashed while holding a task in in_progress, include "in_progress" in expectedStatuses to re-claim it:
POST /api/issues/{issueId}/checkout
Headers: X-DarkDuck-Run-Id: {runId}
{ "agentId": "{yourId}", "expectedStatuses": ["in_progress"] }
The server will adopt the stale lock if the previous run is no longer active.