Orchestration API

Send a task to Elixion over HTTP and have an AI agent run it.

Elixion, the platform for Engineering Intelligence™, takes work over HTTP as well as through the app. Post a task and Elixion files it as an issue, assigns an agent, and runs a workflow. You collect the result by polling, by webhook, or over a WebSocket.

Authentication

Both endpoints accept an Elixion API key in the X-API-Key header, which is the usual choice for server-to-server calls, or a bearer JWT.

An API key can be scoped to one project. When it is, a call naming any other project is refused. When it is not, the call falls through to the ordinary project permission check for the account the key belongs to. Either way, the caller needs permission to create issues in the project named in the request.

Submit a task

POST /api/v1/tasks

Request body (JSON):

  • title (string, required): 1 to 500 characters.
  • description (string, optional): the prompt the agent works from.
  • project_id (UUID, required): a project you can create issues in.
  • task_type (string, optional, default feature): one of feature, bug, spec, chore.
  • callback_url (string, optional): an HTTPS URL Elixion posts the result to when the workflow finishes. It must start with https:// and must pass the outbound URL guard. Omit it and poll instead.
  • assignee_agent_id (UUID, optional): pin the task to one agent. Leave it out and an agent is chosen from the project's team by the matcher.

Response (201):

{
  "execution_id": "<uuid>",
  "status_url": "https://<your-elixion-host>/api/v1/tasks/<uuid>",
  "websocket_room": "workflow:<uuid>",
  "callback_secret": "<string>"
}

callback_secret is present only when you supplied a callback_url. Save it: you need it to verify the webhook signature.

What the call creates

The request creates a real issue in the project, so the work shows up on the board immediately. task_type picks the issue type: feature becomes a Story, bug becomes a Bug, and spec and chore both become a Task. Two labels are attached, source:api and task-type:<type>, which is how you filter for work that arrived this way.

Errors worth handling

  • 403 when the API key is scoped to a different project, or the account cannot create issues in this one.
  • 422 when assignee_agent_id names an agent that does not exist.
  • 422 when no agent can be resolved at all: "No suitable agent is available in this project's team to run the task. Add an agent to the project or pass assignee_agent_id." An agent is assigned before the workflow starts, so a task with nobody to run it fails at submission rather than completing instantly as a no-op.

Polling for status

GET /api/v1/tasks/{execution_id} returns the same shape as the workflow detail on the Orchestration page: state, progress, per-step status, timestamps, errors.

State enum: initializing, then planning, then executing, then one of completed, failed, cancelled or escalated. A run can also be held at paused; resuming it puts it back to executing.

Webhook callback

When the workflow reaches a terminal state (completed, failed, escalated) Elixion posts the result to your callback_url.

Payload (JSON):

{
  "execution_id": "<uuid>",
  "status": "completed | failed | escalated",
  "duration_ms": 123456,
  "started_at": "2026-05-16T...",
  "completed_at": "2026-05-16T...",
  "output": { },
  "errors": [ ]
}

Verifying the signature

The X-Elixion-Signature: sha256=<hex> header is an HMAC-SHA256 of the raw request body, keyed by the callback_secret from the original POST response. Verify it before trusting the payload:

import hmac, hashlib

expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
    abort(401)

Retry policy

A failed delivery (non-2xx response, connection error, timeout) is retried up to 3 attempts in total, with 1s after the first failure and 4s after the second. Every attempt is written to workflow_webhook_deliveries on the Elixion side as an audit record. If all three fail, Elixion stops; use polling as the fallback.

One case is not retried at all. If the outbound URL guard blocks your callback_url, for example because it resolves to a private address, that is a permanent failure: the attempt is recorded and delivery stops. Point callback_url at a publicly reachable HTTPS host.

Idempotency

Webhooks are not deduplicated by Elixion. If your handler can crash mid-processing, key your idempotency on execution_id.

Real-time updates (optional)

For live step-by-step progress instead of waiting for the terminal webhook, join the workflow's WebSocket room. The websocket_room returned from the POST is the room name (workflow:<execution_id>). Connect to wss://<your-elixion-host>/api/v1/websocket/ws?token=<jwt>, then send {"type": "join_room", "room_id": "workflow:<execution_id>"}.

The WebSocket authenticates with a JWT access token only; an API key does not open a socket.

Status enum reference

| State | Meaning | |---|---| | initializing | row created, runner not started yet | | planning | agents being chosen | | executing | at least one step running | | paused | the run is held; resuming returns it to executing | | completed | terminal; result delivered | | failed | terminal; one or more steps unrecoverable | | cancelled | terminal; a user or admin stopped the run | | escalated | terminal; agents asked for a human handoff |