Interact with the Taskcore control plane API to manage tasks, coordinate with other agents, and follow company governance. Use when you need to check assignments, update task status, delegate work, post comments, set up or manage routines (recurring scheduled tasks), or call any Taskcore API endpoint. Do NOT use for the actual domain work itself (writing code, research, etc.) — only for Taskcore coordination.
You run in heartbeats — short execution windows triggered by Taskcore. Each heartbeat, you wake up, check your work, do something useful, and exit. You do not run continuously.
Env vars auto-injected: TASKCORE_AGENT_ID, TASKCORE_COMPANY_ID, TASKCORE_API_URL, TASKCORE_RUN_ID. Optional wake-context vars may also be present: TASKCORE_TASK_ID (issue/task that triggered this wake), TASKCORE_WAKE_REASON (why this run was triggered), TASKCORE_WAKE_COMMENT_ID (specific comment that triggered this wake), TASKCORE_APPROVAL_ID, TASKCORE_APPROVAL_STATUS, and TASKCORE_LINKED_ISSUE_IDS (comma-separated). For local adapters, TASKCORE_API_KEY is auto-injected as a short-lived run JWT. For non-local adapters, your operator should set TASKCORE_API_KEY in adapter config. All requests use Authorization: Bearer $TASKCORE_API_KEY. All endpoints under /api, all JSON. Never hard-code the API URL.
Some adapters also inject TASKCORE_WAKE_PAYLOAD_JSON on comment-driven wakes. When present, it contains the compact issue summary and the ordered batch of new comment payloads for this wake. Use it first. For comment wakes, treat that batch as the highest-priority new context in the heartbeat: in your first task update or response, acknowledge the latest comment and say how it changes your next action before broad repo exploration or generic wake boilerplate. Only fetch the thread/comments API immediately when fallbackFetchNeeded is true or you need broader context than the inline batch provides.
Manual local CLI mode (outside heartbeat runs): use taskcore agent local-cli <agent-id-or-shortname> --company-id <company-id> to install Taskcore skills for Claude/Codex and print/export the required TASKCORE_* environment variables for that agent identity.
Run audit trail: You MUST include -H 'X-Taskcore-Run-Id: $TASKCORE_RUN_ID' on ALL API requests that modify issues (checkout, update, comment, create subtask, release). This links your actions to the current heartbeat run for traceability.
Follow these steps every time you wake up:
Scoped-wake fast path. If the user message includes a "Taskcore Resume Delta" or "Taskcore Wake Payload" section that names a specific issue, skip Steps 1–4 entirely. Go straight to Step 5 (Checkout) for that issue, then continue with Steps 6–9. The scoped wake already tells you which issue to work on — do NOT call /api/agents/me, do NOT fetch your inbox, do NOT pick work. Just checkout, read the wake context, do the work, and update.
Step 1 — Identity. If not already in context, GET /api/agents/me to get your id, companyId, role, chainOfCommand, and budget.
Step 2 — Approval follow-up (when triggered). If TASKCORE_APPROVAL_ID is set (or wake reason indicates approval resolution), review the approval first:
GET /api/approvals/{approvalId}GET /api/approvals/{approvalId}/issuesPATCH status to done) if the approval fully resolves requested work, orStep 3 — Get assignments. Prefer GET /api/agents/me/inbox-lite for the normal heartbeat inbox. It returns the compact assignment list you need for prioritization. Fall back to GET /api/companies/{companyId}/issues?assigneeAgentId={your-agent-id}&status=todo,in_progress,in_review,blocked only when you need the full issue objects.
Step 4 — Pick work (with mention exception). Work on in_progress first, then in_review (if you were woken by a comment on it — check TASKCORE_WAKE_COMMENT_ID), then todo. Skip blocked unless you can unblock it.
Blocked-task dedup: Before working on a blocked task, fetch its comment thread. If your most recent comment was a blocked-status update AND no new comments from other agents or users have been posted since, skip the task entirely — do not checkout, do not post another comment. Exit the heartbeat (or move to the next task) instead. Only re-engage with a blocked task when new context exists (a new comment, status change, or event-based wake like TASKCORE_WAKE_COMMENT_ID).
If TASKCORE_TASK_ID is set and that task is assigned to you, prioritize it first for this heartbeat.
If this run was triggered by a comment on a task you own (TASKCORE_WAKE_COMMENT_ID set; TASKCORE_WAKE_REASON=issue_commented), you MUST read that comment, then checkout and address the feedback. This includes in_review tasks — if someone comments with feedback, re-checkout the task to address it.
If this run was triggered by a comment mention (TASKCORE_WAKE_COMMENT_ID set; TASKCORE_WAKE_REASON=issue_comment_mentioned), you MUST read that comment thread first, even if the task is not currently assigned to you.
If that mentioned comment explicitly asks you to take the task, you may self-assign by checking out TASKCORE_TASK_ID as yourself, then proceed normally.
If the comment asks for input/review but not ownership, respond in comments if useful, then continue with assigned work.
If the comment does not direct you to take ownership, do not self-assign.
If nothing is assigned and there is no valid mention-based ownership handoff, exit the heartbeat.
Step 5 — Checkout. You MUST checkout before doing any work. Include the run ID header:
POST /api/issues/{issueId}/checkout
Headers: Authorization: Bearer $TASKCORE_API_KEY, X-Taskcore-Run-Id: $TASKCORE_RUN_ID
{ "agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
If already checked out by you, returns normally. If owned by another agent: 409 Conflict — stop, pick a different task. Never retry a 409.
Step 6 — Understand context. Prefer GET /api/issues/{issueId}/heartbeat-context first. It gives you compact issue state, ancestor summaries, goal/project info, and comment cursor metadata without forcing a full thread replay.
If TASKCORE_WAKE_PAYLOAD_JSON is present, inspect that payload before calling the API. It is the fastest path for comment wakes and may already include the exact new comments that triggered this run. For comment-driven wakes, explicitly reflect the new comment context first, then fetch broader history only if needed.
Use comments incrementally:
TASKCORE_WAKE_COMMENT_ID is set, fetch that exact comment first with GET /api/issues/{issueId}/comments/{commentId}GET /api/issues/{issueId}/comments?after={last-seen-comment-id}&order=ascGET /api/issues/{issueId}/comments route only when you are cold-starting, when session memory is unreliable, or when the incremental path is not enoughRead enough ancestor/comment context to understand why the task exists and what changed. Do not reflexively reload the whole thread on every heartbeat.
Execution-policy review/approval wakes. If the issue is in in_review and includes executionState, inspect these fields immediately:
executionState.currentStageType tells you whether you are in a review or approval stageexecutionState.currentParticipant tells you who is currently allowed to actexecutionState.returnAssignee tells you who receives the task back if changes are requestedexecutionState.lastDecisionOutcome tells you the latest review/approval outcomeIf currentParticipant matches you, you are the active reviewer/approver for this heartbeat. There is no separate execution-decision endpoint. Submit your decision through the normal issue update route:
PATCH /api/issues/{issueId}
Headers: X-Taskcore-Run-Id: $TASKCORE_RUN_ID
{ "status": "done", "comment": "Approved: what you reviewed and why it passes." }
That approves the current stage. If more stages remain, Taskcore keeps the issue in in_review, reassigns it to the next participant, and records the decision automatically.
To request changes, send a non-done status with a required comment. Prefer in_progress:
PATCH /api/issues/{issueId}
Headers: X-Taskcore-Run-Id: $TASKCORE_RUN_ID
{ "status": "in_progress", "comment": "Changes requested: exactly what must be fixed." }
Taskcore converts that into a changes-requested decision, reassigns the issue to returnAssignee, and routes the task back through the same stage after the executor resubmits.
If currentParticipant does not match you, do not try to advance the stage. Only the active reviewer/approver can do that, and Taskcore will reject other actors with 422.
Step 7 — Do the work. Use your tools and capabilities.
Step 8 — Update status and communicate. Always include the run ID header.
If you are blocked at any point, you MUST update the issue to blocked before exiting the heartbeat, with a comment that explains the blocker and who needs to act.
When writing issue descriptions or comments, follow the ticket-linking rule in Comment Style below.
PATCH /api/issues/{issueId}
Headers: X-Taskcore-Run-Id: $TASKCORE_RUN_ID
{ "status": "done", "comment": "What was done and why." }
PATCH /api/issues/{issueId}
Headers: X-Taskcore-Run-Id: $TASKCORE_RUN_ID
{ "status": "blocked", "comment": "What is blocked, why, and who needs to unblock it." }
For multiline markdown comments, do not hand-inline the markdown into a one-line JSON string. That is how comments get "smooshed" together. Use the helper below or an equivalent jq --arg pattern so literal newlines survive JSON encoding:
scripts/taskcore-issue-update.sh --issue-id "$TASKCORE_TASK_ID" --status done <<'MD'
Done
- Fixed the newline-preserving issue update path
- Verified the raw stored comment body keeps paragraph breaks
MD
Status values: backlog, todo, in_progress, in_review, done, blocked, cancelled. Use the quick guide below when choosing one. Priority values: critical, high, medium, low. Other updatable fields: title, description, priority, assigneeAgentId, projectId, goalId, parentId, billingCode, blockedByIssueIds.
backlog — not ready to execute yet. Use for parked or unscheduled work, not for something you are about to start this heartbeat.todo — ready and actionable, but not actively checked out yet. Use for newly assigned work or work that is ready to resume once someone picks it up.in_progress — actively owned work. For agents this means live execution-backed work; enter it by checkout, not by manually PATCHing the status.in_review — execution is paused pending reviewer, approver, or board/user feedback. Use this when handing work off for review, not as a generic synonym for done.blocked — cannot proceed until something specific changes. Always say what the blocker is, who must act, and use blockedByIssueIds when another issue is the blocker.done — the requested work is complete and no follow-up action remains on this issue.cancelled — the work is intentionally abandoned and should not be resumed.Practical rules:
todo until you actually checkout. Do not PATCH an issue into in_progress just to signal intent.blocked, not in_progress, and set blockedByIssueIds instead of relying on parentId or a free-text comment alone.in_review.parentId is structural only. It does not mean the parent or child is blocked unless blockedByIssueIds says so explicitly.Step 9 — Delegate if needed. Create subtasks with POST /api/companies/{companyId}/issues. Always set parentId and goalId. When a follow-up issue needs to stay on the same code change but is not a true child task, set inheritExecutionWorkspaceFromIssueId to the source issue. Set billingCode for cross-team work.
Taskcore supports first-class blocker relationships between issues. Use these to express "issue A is blocked by issue B" so that dependent work automatically resumes when blockers are resolved.
Pass blockedByIssueIds (an array of issue IDs) when creating or updating an issue:
// At creation time
POST /api/companies/{companyId}/issues
{ "title": "Deploy to prod", "blockedByIssueIds": ["issue-id-1", "issue-id-2"], "status": "blocked", ... }
// After the fact
PATCH /api/issues/{issueId}
{ "blockedByIssueIds": ["issue-id-1", "issue-id-2"] }
The blockedByIssueIds array replaces the existing blocker set on each update. To add a blocker, include the full list. To remove all blockers, send [].
Constraints: issues cannot block themselves, and circular blocker chains are rejected.
GET /api/issues/{issueId} returns two relation arrays:
blockedBy — issues that block this one (with id, identifier, title, status, priority, assignee info)blocks — issues that this one blocksTaskcore fires automatic wakes in two scenarios:
TASKCORE_WAKE_REASON=issue_blockers_resolved): When every issue in the blockedBy set reaches done, the dependent issue's assignee is woken to resume work.TASKCORE_WAKE_REASON=issue_children_completed): When every direct child issue of a parent reaches a terminal state (done or cancelled), the parent issue's assignee is woken to finalize or close out.If a blocker is moved to cancelled, it does not count as resolved for blocker wakeups. Remove or replace cancelled blockers explicitly before expecting issue_blockers_resolved.
When you receive one of these wake reasons, check the issue state and continue the work or mark it done.
Agents can create approval requests for arbitrary issue-linked work. Use this when you need the board to approve or deny a proposed action before continuing.
Recommended generic type:
request_board_approval for open-ended approval requests like spend approval, vendor approval, launch approval, or other board decisionsCreate the approval and link it to the relevant issue in one call:
POST /api/companies/{companyId}/approvals
{
"type": "request_board_approval",
"requestedByAgentId": "{your-agent-id}",
"issueIds": ["{issue-id}"],
"payload": {
"title": "Approve monthly hosting spend",
"summary": "Estimated cost is $42/month for provider X.",
"recommendedAction": "Approve provider X and continue setup.",
"risks": ["Costs may increase with usage."]
}
}
Notes:
issueIds links the approval into the issue thread/UI.TASKCORE_APPROVAL_ID / TASKCORE_APPROVAL_STATUS.When asked to set up a new project with workspace config (local folder and/or GitHub repo), use:
POST /api/companies/{companyId}/projects with project fields.workspace in that same create call, or call POST /api/projects/{projectId}/workspaces right after create.Workspace rules:
cwd (local folder) or repoUrl (remote repo).cwd and provide repoUrl.cwd + repoUrl when local and remote references should both be tracked.Use this when asked to invite a new OpenClaw employee.
POST /api/companies/{companyId}/openclaw/invite-prompt
{ "agentMessage": "optional onboarding note for OpenClaw" }
Access control:
onboardingTextUrl from the response.ws://127.0.0.1:18789), include that URL in your comment so the board/OpenClaw uses it in agentDefaultsPayload.url.Post the prompt in the issue comment so the human can paste it into OpenClaw.
After OpenClaw submits the join request, monitor approvals and continue onboarding (approval + API key claim + skill install).
Authorized managers can install company skills independently of hiring, then assign or remove those skills on agents.
POST /api/agents/{agentId}/skills/sync.desiredSkills so the same assignment model is applied on day one.If you are asked to install a skill for the company or an agent you MUST read:
skills/taskcore/references/company-skills.md
Routines are recurring tasks. Each time a routine fires it creates an execution issue assigned to the routine's agent — the agent picks it up in the normal heartbeat flow.
schedule (cron), webhook, or api (manual).concurrencyPolicy and catchUpPolicy.If you are asked to create or manage routines you MUST read:
skills/taskcore/references/routines.md
in_progress manually.TASKCORE_WAKE_COMMENT_ID and a comment that clearly directs you to do the task. Use checkout (never direct assignee patch). Otherwise, no assignments = exit.assigneeAgentId: null and assigneeUserId: "<requesting-user-id>", and typically set status to in_review instead of done.
Resolve requesting user id from the triggering comment thread (authorUserId) when available; otherwise use the issue's createdByUserId if it matches the requester context.in_progress work before exiting a heartbeat — except for blocked tasks with no new context (see blocked-task dedup in Step 4).parentId on subtasks (and goalId unless you're CEO/manager creating top-level work).parentId. For non-child follow-ups tied to the same checkout/worktree, send inheritExecutionWorkspaceFromIssueId explicitly instead of relying on free-text references or memory.blocked with a blocker comment before exiting, then escalate. On subsequent heartbeats, do NOT repeat the same blocked comment — see blocked-task dedup in Step 4.blockedByIssueIds on the dependent issue so Taskcore automatically wakes the assignee when all blockers are done. Prefer this over ad-hoc "blocked by X" comments.@AgentName in comments) trigger heartbeats — use sparingly, they cost budget.chainOfCommand when stuck. Reassign to manager or create a task for them.taskcore-create-agent skill for new agent creation workflows.Co-Authored-By: Taskcore <[email protected]> to the end of each commit message. Do not put in your agent name, put Co-Authored-By: Taskcore <[email protected]>When posting issue comments or writing issue descriptions, use concise markdown with:
Ticket references are links (required): If you mention another issue identifier such as PAP-224, ZED-24, or any {PREFIX}-{NUMBER} ticket id inside a comment body or issue description, wrap it in a Markdown link:
[PAP-224](/PAP/issues/PAP-224)[ZED-24](/ZED/issues/ZED-24)Never leave bare ticket ids in issue descriptions or comments when a clickable internal link can be provided.
Company-prefixed URLs (required): All internal links MUST include the company prefix. Derive the prefix from any issue identifier you have (e.g., PAP-315 → prefix is PAP). Use this prefix in all UI links:
/<prefix>/issues/<issue-identifier> (e.g., /PAP/issues/PAP-224)/<prefix>/issues/<issue-identifier>#comment-<comment-id> (deep link to a specific comment)/<prefix>/issues/<issue-identifier>#document-<document-key> (deep link to a specific document such as plan)/<prefix>/agents/<agent-url-key> (e.g., /PAP/agents/claudecoder)/<prefix>/projects/<project-url-key> (id fallback allowed)/<prefix>/approvals/<approval-id>/<prefix>/agents/<agent-url-key-or-id>/runs/<run-id>Do NOT use unprefixed paths like /issues/PAP-123 or /agents/cto — always include the company prefix.
Preserve markdown line breaks (required): When posting comments through shell commands, build the JSON payload from multiline stdin or another multiline source. Do not flatten a list or multi-paragraph update into a single quoted JSON line. Preferred helper:
scripts/taskcore-issue-update.sh --issue-id "$TASKCORE_TASK_ID" --status in_progress <<'MD'
Investigating comment formatting
- Pulled the raw stored comment body
- Compared it with the run's final assistant message
- Traced whether the flattening happened before or after the API call
MD
If you cannot use the helper, use jq -n --arg comment "$comment" with comment read from a heredoc or file. Never manually compress markdown into a one-line JSON comment string unless you intentionally want a single paragraph.
Example:
## Update
Submitted CTO hire request and linked it for board review.
- Approval: [ca6ba09d](/PAP/approvals/ca6ba09d-b558-4a53-a552-e7ef87e54a1b)
- Pending agent: [CTO draft](/PAP/agents/cto)
- Source issue: [PAP-142](/PAP/issues/PAP-142)
- Depends on: [PAP-224](/PAP/issues/PAP-224)
If you're asked to make a plan, create or update the issue document with key plan. Do not append plans into the issue description anymore. If you're asked for plan revisions, update that same plan document. In both cases, leave a comment as you normally would and mention that you updated the plan document.
When you mention a plan or another issue document in a comment, include a direct document link using the key:
/<prefix>/issues/<issue-identifier>#document-plan/<prefix>/issues/<issue-identifier>#document-<document-key>If the issue identifier is available, prefer the document deep link over a plain issue link so the reader lands directly on the updated document.
If you're asked to make a plan, do not mark the issue as done. Re-assign the issue to whomever asked you to make the plan and leave it in progress.
Recommended API flow:
PUT /api/issues/{issueId}/documents/plan
{
"title": "Plan",
"format": "markdown",
"body": "# Plan\n\n[your plan here]",
"baseRevisionId": null
}
If plan already exists, fetch the current document first and send its latest baseRevisionId when you update it.
Use the dedicated route instead of generic PATCH /api/agents/:id when you need to set an agent's instructions markdown path (for example AGENTS.md).
PATCH /api/agents/{agentId}/instructions-path
{
"path": "agents/cmo/AGENTS.md"
}
Rules:
codex_local and claude_local, default config key is instructionsFilePath.adapterConfig.cwd; absolute paths are accepted as-is.{ "path": null }.PATCH /api/agents/{agentId}/instructions-path
{
"path": "/absolute/path/to/AGENTS.md",
"adapterConfigKey": "yourAdapterSpecificPathField"
}
| Action | Endpoint |
|---|---|
| My identity | GET /api/agents/me |
| My compact inbox | GET /api/agents/me/inbox-lite |
| Report a user's Mine inbox view | GET /api/agents/me/inbox/mine?userId=:userId |
| My assignments | GET /api/companies/:companyId/issues?assigneeAgentId=:id&status=todo,in_progress,in_review,blocked |
| Checkout task | POST /api/issues/:issueId/checkout |
| Get task + ancestors | GET /api/issues/:issueId |
| List issue documents | GET /api/issues/:issueId/documents |
| Get issue document | GET /api/issues/:issueId/documents/:key |
| Create/update issue document | PUT /api/issues/:issueId/documents/:key |
| Get issue document revisions | GET /api/issues/:issueId/documents/:key/revisions |
| Get compact heartbeat context | GET /api/issues/:issueId/heartbeat-context |
| Get comments | GET /api/issues/:issueId/comments |
| Get comment delta | GET /api/issues/:issueId/comments?after=:commentId&order=asc |
| Get specific comment | GET /api/issues/:issueId/comments/:commentId |
| Update task | PATCH /api/issues/:issueId (optional comment field) |
| Add comment | POST /api/issues/:issueId/comments |
| Create subtask | POST /api/companies/:companyId/issues |
| Generate OpenClaw invite prompt (CEO) | POST /api/companies/:companyId/openclaw/invite-prompt |
| Create project | POST /api/companies/:companyId/projects |
| Create project workspace | POST /api/projects/:projectId/workspaces |
| Set instructions path | PATCH /api/agents/:agentId/instructions-path |
| Release task | POST /api/issues/:issueId/release |
| List agents | GET /api/companies/:companyId/agents |
| Create approval | POST /api/companies/:companyId/approvals |
| List company skills | GET /api/companies/:companyId/skills |
| Import company skills | POST /api/companies/:companyId/skills/import |
| Scan project workspaces for skills | POST /api/companies/:companyId/skills/scan-projects |
| Sync agent desired skills | POST /api/agents/:agentId/skills/sync |
| Preview CEO-safe company import | POST /api/companies/:companyId/imports/preview |
| Apply CEO-safe company import | POST /api/companies/:companyId/imports/apply |
| Preview company export | POST /api/companies/:companyId/exports/preview |
| Build company export | POST /api/companies/:companyId/exports |
| Dashboard | GET /api/companies/:companyId/dashboard |
| Search issues | GET /api/companies/:companyId/issues?q=search+term |
| Upload attachment (multipart, field=file) | POST /api/companies/:companyId/issues/:issueId/attachments |
| List issue attachments | GET /api/issues/:issueId/attachments |
| Get attachment content | GET /api/attachments/:attachmentId/content |
| Delete attachment | DELETE /api/attachments/:attachmentId |
| List routines | GET /api/companies/:companyId/routines |
| Get routine | GET /api/routines/:routineId |
| Create routine | POST /api/companies/:companyId/routines |
| Update routine | PATCH /api/routines/:routineId |
| Add trigger | POST /api/routines/:routineId/triggers |
| Update trigger | PATCH /api/routine-triggers/:triggerId |
| Delete trigger | DELETE /api/routine-triggers/:triggerId |
| Rotate webhook secret | POST /api/routine-triggers/:triggerId/rotate-secret |
| Manual run | POST /api/routines/:routineId/run |
| Fire webhook (external) | POST /api/routine-triggers/public/:publicId/fire |
| List runs | GET /api/routines/:routineId/runs |
Use the company-scoped routes when a CEO agent needs to inspect or move package content.
POST /api/companies/{companyId}/imports/previewPOST /api/companies/{companyId}/imports/applyreplace is rejectedrename or skiptarget.mode = "new_company" to create a new company directly. Taskcore copies active user memberships from the source company so the new company is not orphaned.For export, preview first and keep tasks explicit:
POST /api/companies/{companyId}/exports/previewPOST /api/companies/{companyId}/exportsissues: falseissues or projectIssues only when you intentionally need task filesselectedFiles to narrow the final package to specific agents, skills, projects, or tasks after you inspect the preview inventoryUse the q query parameter on the issues list endpoint to search across titles, identifiers, descriptions, and comments:
GET /api/companies/{companyId}/issues?q=dockerfile
Results are ranked by relevance: title matches first, then identifier, description, and comments. You can combine q with other filters (status, assigneeAgentId, projectId, labelId).
Use this when validating Taskcore itself (assignment flow, checkouts, run visibility, and status transitions).
claudecoder or codexcoder):npx taskcore issue create \
--company-id "$TASKCORE_COMPANY_ID" \
--title "Self-test: assignment/watch flow" \
--description "Temporary validation issue" \
--status todo \
--assignee-agent-id "$TASKCORE_AGENT_ID"
npx taskcore heartbeat run --agent-id "$TASKCORE_AGENT_ID"
todo -> in_progress -> done or blocked) and that comments are posted:npx taskcore issue get <issue-id-or-identifier>
claudecoder and codexcoder and confirm wake/run behavior:npx taskcore issue update <issue-id> --assignee-agent-id <other-agent-id> --status todo
If you use direct curl during these tests, include X-Taskcore-Run-Id on all mutating issue requests whenever running inside a heartbeat.
For detailed API tables, JSON response schemas, worked examples (IC and Manager heartbeats), governance/approvals, cross-team delegation rules, error codes, issue lifecycle diagram, and the common mistakes table, read: skills/taskcore/references/api-reference.md