Skip to main content

AI Agents

Stateway is designed to be consumed directly by AI agents via REST. An agent can start a process, query its state, complete human tasks, and send events — all through straightforward HTTP calls.

This page covers the complete integration pattern with ready-to-use JSON payloads suitable for LLM tool calls.


Authentication​

Every request requires an API Key in the X-API-Key header. The key resolves the tenant automatically — you never pass a tenant_id.

X-API-Key: sw_live_<key>

Core Operations​

1. Start a Process​

Instantiate a process definition by its key. Pass any initial variables the process needs.

POST /v1/instances
Content-Type: application/json
X-API-Key: sw_live_<key>

{
"definition_key": "loan-approval",
"variables": {
"applicantId": "usr_123",
"amount": 5000,
"currency": "BRL"
}
}

Response:

{
"data": {
"id": "inst_abc123",
"tenantId": "ten_abc123",
"definitionId": "def_abc123",
"definitionHash": "a3f1c2d4e5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2",
"correlationId": null,
"status": "running",
"variables": {
"applicantId": "usr_123",
"amount": 5000,
"currency": "BRL"
},
"startedAt": "2026-04-01T10:00:00Z",
"endedAt": null,
"error": null,
"metadata": {}
}
}

Save data.id — you'll use it for all subsequent calls on this instance.


2. Query Instance State​

Check whether the process is still running and what the current variable values are.

GET /v1/instances/{instance_id}
X-API-Key: sw_live_<key>

Response:

{
"data": {
"id": "inst_abc123",
"status": "running",
"variables": {
"applicantId": "usr_123",
"amount": 5000,
"decision": null
},
"startedAt": "2026-04-01T10:00:00Z",
"endedAt": null
}
}

status is one of: pending, running, suspended, completed, terminated, error.

To find out which element the process is currently waiting on, query its tokens:

GET /v1/instances/{instance_id}/tokens
X-API-Key: sw_live_<key>
{
"data": [
{ "id": "tok_1", "elementId": "start", "elementType": "startEvent", "status": "completed" },
{ "id": "tok_2", "elementId": "approve-loan-task", "elementType": "userTask", "status": "waiting" }
]
}

A token with status: "waiting" is where the process is currently paused.


3. List Pending Tasks​

Retrieve human tasks that are waiting for action — filtered by instance, status, assignee, or candidate group.

GET /v1/tasks?instance_id=inst_abc123&status=pending
X-API-Key: sw_live_<key>

Response:

{
"data": [
{
"id": "task_xyz789",
"instanceId": "inst_abc123",
"elementId": "approve-loan-task",
"name": "Approve Loan",
"status": "pending",
"assignee": null,
"candidateGroups": ["credit-team"],
"candidateUsers": [],
"variables": {
"amount": 5000
},
"createdAt": "2026-04-01T10:00:05Z",
"claimedAt": null,
"completedAt": null
}
]
}

4. Complete a Task​

Submit the outcome of a human task. The variables object is merged into the existing instance variables — you only need to send what you're setting.

POST /v1/tasks/{task_id}/complete
Content-Type: application/json
X-API-Key: sw_live_<key>

{
"variables": {
"decision": "approved",
"reviewedBy": "agent-gpt4o",
"reviewNotes": "Income verified. Risk score within acceptable range."
}
}

Response:

{
"data": {
"id": "task_xyz789",
"status": "completed"
}
}

Completing an already-completed task returns 404 NOT_FOUND ("Task already completed") — check status first if you're not sure.


5. Send an Event to an Instance​

Deliver an external signal to a specific instance — a payment confirmation, a webhook callback result, an async service response. The event resolves only the waiting tokens that declare the event_type as their name (a named intermediateCatchEvent, a message boundary event, or an async service task's callbackEvent). It never completes a user task; without event_type, nothing advances — see the Events and Signals guide for the exact rules.

POST /v1/instances/{instance_id}/events
Content-Type: application/json
X-API-Key: sw_live_<key>

{
"event_type": "payment_confirmed",
"variables": {
"transactionId": "txn_9981",
"paidAt": "2026-04-01T10:10:00Z"
}
}

Response:

{
"data": {
"event_type": "payment_confirmed",
"processed": true
}
}

Full error conditions (instance not found, no waiting or active token, instance already finished) are covered in the Events and Signals guide.


6. Broadcast a Signal​

Broadcast a signal to every waiting token across the tenant's running instances — this is a different endpoint from instance events, and does not take an instance ID. The signal_name is echoed back but not matched against any element (name-based matching is planned, not yet supported), so every waiting token is completed regardless of what it is waiting for.

POST /v1/events/signal/{signal_name}
Content-Type: application/json
X-API-Key: sw_live_<key>

{
"variables": {
"productId": "prod-001",
"newPrice": 99.99
}
}

Response:

{
"data": {
"signal_name": "price_update",
"processed": 3,
"failed": 0,
"errors": []
}
}

See the Events and Signals guide for how partial failures are reported.


Tool Definitions for LLMs​

The examples below are ready to paste into your LLM's tool schema. They follow the JSON Schema format used by the OpenAI/Anthropic tool call APIs, with parameter names matching the REST API's own field names one-to-one — no translation layer needed. If you use the Stateway MCP server instead, its tools have their own parameter names; see the MCP guide.

start_process​

{
"name": "start_process",
"description": "Start a new instance of a Stateway process definition. Returns the instance ID needed for all subsequent operations.",
"parameters": {
"type": "object",
"properties": {
"definition_key": {
"type": "string",
"description": "The process definition key (e.g. 'loan-approval', 'onboarding')"
},
"variables": {
"type": "object",
"description": "Initial process variables as key-value pairs",
"additionalProperties": true
}
},
"required": ["definition_key"]
}
}

get_instance​

{
"name": "get_instance",
"description": "Query the current state of a process instance: status and variable values.",
"parameters": {
"type": "object",
"properties": {
"instance_id": {
"type": "string",
"description": "The instance ID returned by start_process"
}
},
"required": ["instance_id"]
}
}

list_tasks​

{
"name": "list_tasks",
"description": "List human tasks for a given process instance.",
"parameters": {
"type": "object",
"properties": {
"instance_id": {
"type": "string",
"description": "Filter tasks by instance ID"
},
"status": {
"type": "string",
"enum": ["pending", "claimed", "completed", "cancelled"],
"description": "Task status filter (default: pending)"
}
},
"required": []
}
}

complete_task​

{
"name": "complete_task",
"description": "Complete a pending human task and supply output variables that will be merged into the process.",
"parameters": {
"type": "object",
"properties": {
"task_id": {
"type": "string",
"description": "The task ID from list_tasks"
},
"variables": {
"type": "object",
"description": "Output variables to merge into the process after task completion",
"additionalProperties": true
}
},
"required": ["task_id"]
}
}

send_event​

{
"name": "send_event",
"description": "Send a named event to a specific process instance. It resolves the waiting tokens that declare event_type as their name (a named intermediateCatchEvent, a message boundary event, or an async service task whose callbackEvent matches). If none declares it, the event is recorded for a later catch and nothing advances. It never completes a user task.",
"parameters": {
"type": "object",
"properties": {
"instance_id": {
"type": "string",
"description": "Target instance ID"
},
"event_type": {
"type": "string",
"description": "Event name (e.g. 'payment_confirmed'), matched against the event names declared by waiting elements in the instance. Without it, nothing advances"
},
"variables": {
"type": "object",
"description": "Variables to merge into the instance",
"additionalProperties": true
}
},
"required": ["instance_id"]
}
}

send_signal​

{
"name": "send_signal",
"description": "Broadcast a signal to every waiting token of every running instance in the tenant. The signal name is not matched against any element: all waiting tokens are completed.",
"parameters": {
"type": "object",
"properties": {
"signal_name": {
"type": "string",
"description": "Signal name; echoed in the response but not matched against any element"
},
"variables": {
"type": "object",
"description": "Variables to attach to the broadcast",
"additionalProperties": true
}
},
"required": ["signal_name"]
}
}

Complete Agent Loop Example​

A typical agent loop for a process that requires an external decision:

import httpx

BASE_URL = "https://api.stateway.io/v1"
HEADERS = {"X-API-Key": "sw_live_<key>", "Content-Type": "application/json"}

# 1. Start the process
resp = httpx.post(f"{BASE_URL}/instances", headers=HEADERS, json={
"definition_key": "credit-analysis",
"variables": {"customerId": "cust_001", "requestedLimit": 10000}
})
instance_id = resp.json()["data"]["id"]

# 2. Poll until a task appears
import time
while True:
tasks = httpx.get(f"{BASE_URL}/tasks", headers=HEADERS,
params={"instance_id": instance_id, "status": "pending"}).json()
if tasks["data"]:
break
time.sleep(2)

task = tasks["data"][0]

# 3. Agent makes a decision and completes the task
decision = run_agent_analysis(task["variables"]) # your LLM call
httpx.post(f"{BASE_URL}/tasks/{task['id']}/complete", headers=HEADERS, json={
"variables": {"decision": decision["result"], "score": decision["score"]}
})

# 4. Wait for the process to finish
while True:
instance = httpx.get(f"{BASE_URL}/instances/{instance_id}", headers=HEADERS).json()["data"]
if instance["status"] in ("completed", "terminated", "error"):
break
time.sleep(2)

print(instance["variables"])

Tips for Agent Implementations​

  • Poll with backoff. Processes can wait for minutes or hours at a human task or timer. Use exponential backoff instead of tight loops — this is the reliable integration pattern today for instance and task state.
  • Webhooks cover a narrower set of events today. Service task completion/failure and a few instance-management operations dispatch webhooks (see the Webhooks guide); general instance and human-task lifecycle changes are not yet dispatched as webhooks, so poll for those.
  • Idempotency. Completing a task twice returns 404 NOT_FOUND. Check status before calling complete_task.
  • Variables are merged. Sending variables in a task completion or an event merges them into the existing instance variables — you don't need to send every variable, only the ones you're setting.
  • Use MCP for richer integrations. If your agent framework supports MCP, the MCP guide describes the Stateway MCP server which exposes these operations as native tools with typed schemas.