Workflows API
List workflow definitions, start a workflow run, read its status and current step, move it on with an event, and cancel it with the Muin Workflows API.
A workflow definition is a process you set up in Muin, such as an invoice approval. Each time it runs, Muin creates a workflow instance that tracks where that run is. The Workflows API lets your system start instances, read their status, move them on, and cancel them.
When you’d use this
- Start an approval in Muin when a record is created in your own system.
- Show the status of a Muin approval in another tool.
- Move a run on, for example to
approved, from your own app. - Cancel a run that is no longer needed.
Before you start
- All paths are on
https://muin-api.falaah.ai/api/v1/workflows. - Send your key as
Authorization: Bearer $MUIN_API_KEY. A Read key can only makeGETrequests. Starting, moving and cancelling a run need Write. - A key acts as the person who created it. That person needs access to Workflows, and the workflow’s module must be turned on for your organization.
Keys, scopes, rate limits and general errors: Authentication.
List workflow definitions
GET /api/v1/workflows/definitions
| Parameter | Type | Required | Description |
|---|---|---|---|
category |
string | No | Only definitions in this category |
document_type |
string | No | Only definitions that support this document type |
limit |
integer | No | 1 to 100. Default 100 |
offset |
integer | No | Rows to skip. Default 0 |
curl "https://muin-api.falaah.ai/api/v1/workflows/definitions?limit=20" \
-H "Authorization: Bearer $MUIN_API_KEY"
{
"workflows": [
{
"id": "3b6f2a10-8c4d-4f0e-9a51-7d2c1e9b4a20",
"name": "Invoice approval",
"slug": "invoice-approval",
"description": "Route an invoice to a manager for approval",
"category": "finance",
"module_code": "finance",
"version": "1.0",
"is_active": true,
"is_template": false,
"tier": "automation",
"dsl": { "...": "the workflow document" },
"created_at": "2026-09-01T16:00:00Z",
"updated_at": "2026-09-20T18:30:00Z"
}
],
"total": 1
}
Use id as workflow_definition_id when you start a run. dsl is the full workflow document, including the events each step accepts.
Get a workflow definition
GET /api/v1/workflows/definitions/{definition_id}
| Parameter | Type | Required | Description |
|---|---|---|---|
definition_id |
string (path) | Yes | The definition’s id or its slug |
curl https://muin-api.falaah.ai/api/v1/workflows/definitions/invoice-approval \
-H "Authorization: Bearer $MUIN_API_KEY"
The response is one definition, with the same fields as an item in the list above, plus latest_version (id, version, status, published_at) when the definition has a version.
Start a workflow instance
POST /api/v1/workflows/instances
| Field | Type | Required | Description |
|---|---|---|---|
workflow_definition_id |
UUID | Yes | The definition to run |
document_id |
UUID | No | A Muin document this run is about |
initial_data |
object | No | Starting data for the run. Default {} |
curl -X POST https://muin-api.falaah.ai/api/v1/workflows/instances \
-H "Authorization: Bearer $MUIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflow_definition_id": "3b6f2a10-8c4d-4f0e-9a51-7d2c1e9b4a20",
"initial_data": { "invoice_number": "INV-1042", "amount": 1250.00 }
}'
Returns 201 with the new instance. The run starts from its first step right away.
{
"id": "9e4d7c21-5a3b-4f86-b0e2-1c8a6f3d2e57",
"tenant_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"workflow_definition_id": "3b6f2a10-8c4d-4f0e-9a51-7d2c1e9b4a20",
"document_id": null,
"started_by_id": "5f6e7d8c-9b0a-4c1d-8e2f-3a4b5c6d7e8f",
"current_state": "manager_review",
"previous_state": "submitted",
"status": "waiting_human",
"status_message": null,
"initial_context": { "invoice_number": "INV-1042", "amount": 1250.0 },
"started_at": "2026-10-09T17:05:12Z",
"completed_at": null,
"due_at": null,
"error_message": null,
"retry_count": 0,
"priority": "medium",
"workflow_definition_version_id": "c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f",
"created_at": "2026-10-09T17:05:12Z",
"updated_at": "2026-10-09T17:05:13Z"
}
Responses also carry state_data, transition_history, checkpoint_data, checkpoint_at, the escalation fields (escalation_level, escalated_to_id, escalated_at), error_details and workflow_definition.
List workflow instances
GET /api/v1/workflows/instances
| Parameter | Type | Required | Description |
|---|---|---|---|
workflow_definition_id |
UUID | No | Only runs of this definition |
document_id |
UUID | No | Only runs about this document |
status |
string | No | Only runs with this status (see Instance statuses) |
module |
string | No | Only runs of definitions in this module, for example compliance |
limit |
integer | No | 1 to 100. Default 50 |
offset |
integer | No | Rows to skip. Default 0 |
curl "https://muin-api.falaah.ai/api/v1/workflows/instances?status=waiting_human&limit=50&offset=0" \
-H "Authorization: Bearer $MUIN_API_KEY"
{
"workflows": [
{
"id": "9e4d7c21-5a3b-4f86-b0e2-1c8a6f3d2e57",
"workflow_definition_id": "3b6f2a10-8c4d-4f0e-9a51-7d2c1e9b4a20",
"current_state": "manager_review",
"status": "waiting_human",
"started_at": "2026-10-09T17:05:12Z",
"waiting_on": {
"task_id": "7a8b9c0d-1e2f-4a3b-9c4d-5e6f7a8b9c0d",
"task_title": "Approve invoice INV-1042",
"assignee_id": "1f2e3d4c-5b6a-4978-8695-a4b3c2d1e0f9",
"assignee_name": "Sarah Ahmed",
"since": "2026-10-09T17:05:13Z",
"due_at": null,
"candidate_assignee_ids": []
}
}
],
"total": 1,
"limit": 50,
"offset": 0
}
Each item has the same fields as the start response. waiting_on names the task and person a waiting run is held on. To page, raise offset by limit until you have read total rows.
Get a workflow instance
GET /api/v1/workflows/instances/{instance_id}
| Parameter | Type | Required | Description |
|---|---|---|---|
instance_id |
UUID (path) | Yes | The instance id |
curl https://muin-api.falaah.ai/api/v1/workflows/instances/9e4d7c21-5a3b-4f86-b0e2-1c8a6f3d2e57 \
-H "Authorization: Bearer $MUIN_API_KEY"
{
"id": "9e4d7c21-5a3b-4f86-b0e2-1c8a6f3d2e57",
"current_state": "manager_review",
"previous_state": "submitted",
"status": "waiting_human",
"transition_history": [ { "...": "one entry per step change" } ],
"document": null,
"human_tasks": [
{
"id": "7a8b9c0d-1e2f-4a3b-9c4d-5e6f7a8b9c0d",
"title": "Approve invoice INV-1042",
"status": "pending",
"priority": "medium",
"assigned_to_id": "1f2e3d4c-5b6a-4978-8695-a4b3c2d1e0f9",
"assigned_to_name": "Sarah Ahmed",
"created_at": "2026-10-09T17:05:13+00:00",
"due_at": null,
"candidate_assignee_ids": []
}
]
}
The detail response has every field of the start response, plus:
| Field | Description |
|---|---|
current_state |
The step the run is on now |
previous_state |
The step it came from |
status |
Overall status (see below) |
document |
id, filename and document_type of the linked document, or null |
human_tasks |
Tasks this run created for people |
Instance statuses
| Status | Meaning |
|---|---|
active |
Running |
waiting |
Waiting before it can continue |
waiting_human |
Waiting on a person’s task |
paused |
Paused, by the pause call below or because something it depends on was turned off |
completed |
Finished |
failed |
Stopped on an error. error_message says why |
cancelled |
Cancelled |
timeout |
Ran out of time |
Move an instance on (approve, reject, other events)
POST /api/v1/workflows/instances/{instance_id}/transition
There is no separate approve or reject call. You send the event the current step accepts, such as approved or rejected. The events are defined in the workflow’s dsl.
| Field | Type | Required | Description |
|---|---|---|---|
instance_id |
UUID (path) | Yes | The instance id |
event |
string | Yes | The event to fire |
data |
object | No | Data passed with the event. Default {} |
curl -X POST https://muin-api.falaah.ai/api/v1/workflows/instances/9e4d7c21-5a3b-4f86-b0e2-1c8a6f3d2e57/transition \
-H "Authorization: Bearer $MUIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "event": "approved", "data": { "note": "Within budget" } }'
Returns the instance after the move, with the same fields as the start response.
When the run is waiting on a person’s task, only that person, or an owner or admin, can move it on. So the key must belong to that person, or to an owner or admin. An owner or admin who moves it on for someone else must say why in data.override_reason. The reason is kept on the run’s history.
Cancel an instance
POST /api/v1/workflows/instances/{instance_id}/cancel
| Parameter | Type | Required | Description |
|---|---|---|---|
instance_id |
UUID (path) | Yes | The instance id |
curl -X POST https://muin-api.falaah.ai/api/v1/workflows/instances/9e4d7c21-5a3b-4f86-b0e2-1c8a6f3d2e57/cancel \
-H "Authorization: Bearer $MUIN_API_KEY"
Returns the instance with "status": "cancelled". A run that is already completed or cancelled cannot be cancelled.
Other instance actions
These take no body (retry takes an optional one) and return the instance.
| Action | Request | Allowed when the status is |
|---|---|---|
| Pause | POST /api/v1/workflows/instances/{instance_id}/pause |
active, waiting or waiting_human |
| Resume | POST /api/v1/workflows/instances/{instance_id}/resume |
paused |
| Retry | POST /api/v1/workflows/instances/{instance_id}/retry |
failed (restarts from its last checkpoint) or cancelled (restarts from the first step). Send {"from_task": "<step>"} to restart a failed run from that step |
Workflow events and webhooks
Workflow runs are not in the list of events that deliver to webhooks today. To follow a run, read it with GET /api/v1/workflows/instances/{instance_id}. A workflow can also post to your server itself with a webhook step. How Muin signs those posts: Webhooks.
When it goes wrong
| Status | When | What to do |
|---|---|---|
400 |
An owner or admin moved on a step waiting for someone else without data.override_reason. |
Add override_reason to data. |
403 |
The key is Read-only on a POST, its creator lacks access to Workflows, the workflow’s module is not on for your organization, or the definition is turned off. On a transition: the step is waiting for someone else. |
Use a Write key from someone with access, turn the definition on, or let the assigned person decide the task. |
404 |
No definition or instance with that ID in your organization. | Check the ID. Definitions also accept the slug. |
409 |
Retry with from_task on a run that is not failed. |
Retry only failed runs from a step. |
422 |
The body is invalid, the run could not start, the event is not accepted at the current step, or the status does not allow the action (for example, cancelling a completed run). | Read current_state and status, then send an event or action that fits. |
500 |
The workflow hit an error while moving on. The run is marked failed. |
Read error_message on the instance, fix the cause, then retry. |
Next step
- Authentication: create the API key these calls need.
- Webhooks: receive Muin events on your server and verify signatures.