Skip to content

Cycles

A cycle is a meaningful chunk of a project — a feature, a release, a phase — and it is the unit that actually executes. Tasks fan out inside a cycle run; they do not run on their own.

  • A project with at least one cycle, usually planned by a project run
  • Agency role (owner, admin, member); a client collaborator with steer rights can execute but not edit
  • Model access configured for the workspace
Terminal window
curl -sS -X POST "$HLIX_BASE_URL/v1/api/cycles" \
-H "x-api-key: $HLIX_API_KEY" \
-H "X-Organization-Id: $HLIX_WORKSPACE_ID" \
-H 'content-type: application/json' \
-d '{"projectId":"'"$PROJECT_ID"'","title":"Health and readiness","description":"Add liveness and readiness endpoints with tests and docs."}'

The response shape — adopted is true only when the orchestrator folded the proposal into the roadmap on this pass:

{"proposed":true,"adopted":false}

projectId, title (1–200), and description (1–10 000) are required.

An adopted proposal becomes a cycle at status triage, and the Triage Agent decides whether it goes any further. That is true of every intake path — chat, the API, Linear, or the project’s founding brief — so a cycle in your list at triage has been created but not yet admitted.

Terminal window
curl -sS -X POST "$HLIX_BASE_URL/v1/api/cycles/$CYCLE_ID/execute" \
-H "x-api-key: $HLIX_API_KEY" \
-H "X-Organization-Id: $HLIX_WORKSPACE_ID"

Expected result:

{"message":"Cycle execution started","threadId":"<workspace-id>:<cycle-id>"}

The optional ?mode= query takes sandbox (default) or desktop.

Execution is gated on status: only draft, planning, failed, and interviewing cycles can be executed. A cycle still in triage is not executable — Triage admits it first. Re-executing a failed or interviewing cycle resets it to draft and returns every failed or blocked task to queued — a retry is a genuine retry, not a second parallel run. That reset is itself a recorded transition, attributed to you.

The response means the run was queued, not finished. If queuing itself fails you get a 502 and nothing was started:

{"error":"The cycle run could not be queued, so it was not started. Nothing is running — try again."}
Status Meaning
triage Created and awaiting the Triage Agent’s decision
draft Admitted, not yet running
interviewing The orchestrator is clarifying requirements with you
planning Decomposing the cycle into tasks
executing Tasks are running
e2e End-to-end verification
pr_created Terminal — the work landed as a pull request
failed Terminal — the cycle failed
canceled Terminal — Triage refused the request

Every status move is a governed transition: it is recorded on the cycle, attributed, and stamped with the platform rules in force at the time. GET /v1/api/cycles/:id returns the whole record as stageHistory, oldest first, alongside a revision counter.

{
"from": "triage",
"to": "draft",
"at": "2026-08-12T09:41:07.220Z",
"actor": { "type": "agent", "id": "triage-agent" },
"ruleSetVersion": "hlix-web-v1",
"reason": "Schema change is understood and reversible."
}
Field What it answers
from / to Which move this was
at When
actor.type Who decided: human, agent, or rule
actor.id Which one — a user id, or a stable agent/rule identifier such as triage-agent, orchestrator, gate-denied
ruleSetVersion Which version of the platform rules governed the move
reason Free text, when the mover supplied one

actor.type is the distinction that matters. human is a person acting through the dashboard or the API. agent is an LLM-driven decision — the Triage Agent forwarding a request, the orchestrator submitting a plan. rule is deterministic platform logic with no model involved, such as a denied approval failing the cycle.

revision is a compare-and-swap counter, incremented once per transition. Two writers racing the same cycle cannot both win: the loser is rejected with the row’s real current revision rather than silently overwriting the winner.

Triage writes two more fields onto every cycle it forwards.

  • priority — an integer derived from the urgency tier Triage judged: 300 urgent, 200 high, 100 normal, 0 low. Ready cycles are dispatched highest first.
  • lane — the risk lane the platform rules resolved from the complexity and risk judgement. Four lanes force a human approval before dispatch even when the project has not opted in.

Both are on the cycle row and in the published contract. Triage covers how each is derived.

To list a project’s cycles in dispatch order rather than creation order:

Terminal window
curl -sS "$HLIX_BASE_URL/v1/api/cycles?projectId=$PROJECT_ID&orderBy=priority" \
-H "x-api-key: $HLIX_API_KEY" \
-H "X-Organization-Id: $HLIX_WORKSPACE_ID"

orderBy accepts createdAt (the default, newest first) or priority (highest first, ties keeping creation order). Any other value is a 400. The dashboard’s cycles grid offers the same two sorts.

GET /v1/api/cycles/:id/stream streams the cycle’s event timeline rather than a status field, so you see the run’s narrative as it happens.

Terminal window
curl -N "$HLIX_BASE_URL/v1/api/cycles/$CYCLE_ID/stream" \
-H "x-api-key: $HLIX_API_KEY" \
-H "X-Organization-Id: $HLIX_WORKSPACE_ID"
  • Unnamed events carry one timeline event row each (entityType, eventType, payload, actorType, traceId, timestamp).
  • event: done fires with {"status":"pr_created"} or {"status":"failed"} when the cycle reaches a terminal status, then the stream closes. Unlike the task stream, this one does announce its ending.
  • event: keepalive with an empty payload arrives roughly every 30 seconds.
  • The stream is bounded at 600 polls of 2 seconds — about 20 minutes. Reconnect for longer runs.

For a non-streaming read of the same material use GET /v1/api/cycles/:id/timeline (cycle plus child-task events) or GET /v1/api/cycles/:id/events (cycle events only).

GET /v1/api/cycles/:id returns the cycle row plus its tasks — each with a dependsOn array — and previewUrl and linearIssueUrl when they exist.

Terminal window
curl -sS "$HLIX_BASE_URL/v1/api/cycles/$CYCLE_ID" \
-H "x-api-key: $HLIX_API_KEY" \
-H "X-Organization-Id: $HLIX_WORKSPACE_ID"

GET /v1/api/cycles?projectId=… lists them.

Operation Endpoint Constraint
Add a task POST /v1/api/cycles/:id/tasks title and description required; dependsOn optional
Edit a task PATCH /v1/api/cycles/:id/tasks/:taskId title, description, sortOrder
Remove a task DELETE /v1/api/cycles/:id/tasks/:taskId Only queued or blocked tasks
Edit the cycle PATCH /v1/api/cycles/:id title, description, targetBranch
Delete the cycle DELETE /v1/api/cycles/:id Only draft or failed cycles

Deleting a running cycle is refused rather than racing the engine:

{"error":"Can only delete cycles in draft or failed status"}

POST /v1/api/cycles/:id/messages sends a message into the cycle thread (content required; agentId addresses one worker’s 1:1 thread; messageId makes it idempotent). While the cycle is interviewing, a message with no agentId can advance the lifecycle — the response carries lifecycleAdvanced and lifecycleReason.

POST /v1/api/cycles/:id/messages/stream is the streaming form. Its SSE events are named, unlike the task stream:

Event Payload
user_message {"id":…,"content":…} — your message, as stored
token A raw text chunk, not JSON-wrapped
tool_start {"name":…,"input":…}
tool_end {"name":…,"output":…}
complete {"id":…,"content":…} — the final assistant message
error {"message":…} — then the stream returns
  • GET /v1/api/cycles/:id/artifacts lists what the run produced.
  • GET /v1/api/cycles/:id/artifacts/:filename returns {"url":…,"key":…}, or the raw bytes with ?download=1.
  • GET /v1/api/cycles/:id/usage returns {"totals":{…},"byRole":[…]} with token counts and cost per agent role.
  • {"error":"Cycle cannot be executed in \"executing\" status"} — it is already running. Watch it instead of starting a second run.
  • {"error":"Cycle not found"} — wrong id, or another workspace. Check X-Organization-Id.
  • {"error":"Project not found for cycle"} — the cycle outlived its project; it cannot be run.
  • {"error":"The cycle run could not be queued, so it was not started. Nothing is running — try again."} — a 502. Nothing was half-started; retry is safe.
  • {"error":"Can only delete tasks in queued or blocked status"} — the task is past the point where removing it is safe.
  • {"proposed":true,"adopted":false} and no cycle appears — the orchestrator did not adopt the proposal. Re-read the roadmap before proposing again.
  • {"error":"Cycle cannot be executed in \"triage\" status"} — the Triage Agent has not admitted it yet. There is no way to execute past that decision.
  • The cycle reads canceled — Triage refused the request. The reason is on the last stageHistory entry.
  • The cycle parks and nothing happens — the project has approval required, or its lane demands one regardless. See Approvals.