Skip to content
Get Started

Safe Workflow Actions for Agents

Use a workflow-backed skill action when an agent needs to change application state. The runtime treats these actions as asynchronous mutations: a successful request returns 202 Accepted and a job URL that the client can observe.

Put the current-state requirement in the workflow, not in the prompt:

[workflow.ClaimIssueForQA]
description = "Compare-and-set an issue from ready_to_test to testing."
trigger = { manual = true }
[workflow.ClaimIssueForQA.precondition]
"variables.data.record.qaState" = "ready_to_test"
[[workflow.ClaimIssueForQA.steps]]
type = "query"
entity = "Issue"
action = "update"
[workflow.ClaimIssueForQA.steps.where]
id = "{{ variables.data.record.id }}"
qaState = "ready_to_test"
[workflow.ClaimIssueForQA.steps.data]
qaState = "testing"
qaRunId = "{{ variables.data.attribution.runId }}"
claimedBy = "{{ variables.data.attribution.agentId }}"

The precondition provides an early conflict response, while the conditional update protects against a concurrent state change. Multi-step mutations should set transactional = true so their writes commit or roll back together. The Node runtime supports database transactions with SQLite and PostgreSQL. Runtime Worker can identify workflows that could eventually compile to an atomic D1 batch, but it does not execute transactional Agent API workflows yet.

Every mutation requires Idempotency-Key. Agent clients should also send X-Agent-Run-Id so audit records can connect the request to one invocation.

Terminal window
curl -i -X POST "$ZEBRIC_URL/api/agent/issues/$ISSUE_ID/claim" \
-H "Authorization: Bearer $ISSUE_BOARD_AGENT_API_KEY" \
-H "Idempotency-Key: claim-$ISSUE_ID-run-42" \
-H "X-Agent-Run-Id: run-42" \
-H "Content-Type: application/json" \
-d '{}'

A successful response identifies a job:

{
"success": true,
"job": {
"id": "01J...",
"workflow": "ClaimIssueForQA",
"status": "pending",
"url": "/api/jobs/01J..."
}
}

Poll the returned job.url with the same credential until the job succeeds or fails. Jobs are isolated to their authenticated owner; another credential cannot observe them.

Repeat an uncertain mutation with the same idempotency key and identical input. The runtime returns the original accepted result rather than executing it twice. Reusing a key with different input returns 409 IDEMPOTENCY_KEY_REUSE.

Zebric Agent retries only when the error envelope explicitly sets retryable: true. It uses at most three total attempts by default, honors Retry-After, and reuses the same idempotency key. Validation, authorization, and state conflicts are not automatically retried.

Common conflict codes are:

CodeMeaningClient response
WORKFLOW_PRECONDITION_FAILEDThe record is no longer in the required stateRefresh the record and reconsider the task
STATE_CONFLICTA conditional state change lost a raceRefresh; do not blindly replay
IDEMPOTENCY_KEY_REUSEThe key was used with different inputTreat as a caller bug and generate keys deterministically per intended operation

See Workflows & Notifications for workflow syntax and REST API & OpenAPI for response and error envelopes.