Workflows API Reference

All API endpoints require authentication and tenant context unless otherwise noted.

Base URL: /api/v1

Workflows

List Workflows

GET /api/v1/workflows

Query Parameters:

Parameter Type Description
status string Filter by status: draft, active, paused, archived
trigger_type string Filter by trigger: manual, schedule, webhook, event
search string Search by name or description
tag string Filter by tag
sort_by string Sort field (default: created_at)
sort_dir string Sort direction: asc, desc (default: desc)
per_page integer Items per page (default: 15)
all boolean Return all without pagination

Response:

{
  "success": true,
  "data": [
    {
      "id": "uuid",
      "name": "Order Confirmation",
      "description": "Send confirmation email when order is created",
      "status": "active",
      "trigger_type": "event",
      "trigger_config": {},
      "settings": {},
      "tags": ["orders", "email"],
      "execution_count": 150,
      "success_count": 145,
      "failure_count": 5,
      "last_executed_at": "2025-01-16T10:00:00Z",
      "created_at": "2025-01-01T00:00:00Z",
      "updated_at": "2025-01-15T00:00:00Z",
      "nodes": [...],
      "connections": [...]
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 5,
    "per_page": 15,
    "total": 72
  }
}

Create Workflow

POST /api/v1/workflows

Request Body:

{
  "name": "Order Confirmation",
  "description": "Send confirmation email when order is created",
  "status": "draft",
  "trigger_type": "event",
  "trigger_config": {
    "event": "order.created"
  },
  "settings": {
    "retry_on_failure": true
  },
  "tags": ["orders", "email"],
  "nodes": [
    {
      "node_type": "orders:order-created",
      "node_category": "trigger",
      "label": "Order Created",
      "config": {},
      "position": { "x": 100, "y": 100 }
    },
    {
      "node_type": "workflows:send-email",
      "node_category": "action",
      "label": "Send Confirmation",
      "config": {
        "to": "{{customer.email}}",
        "subject": "Order {{order.id}} Confirmed",
        "body": "Thank you for your order!"
      },
      "position": { "x": 100, "y": 250 }
    }
  ],
  "connections": [
    {
      "source_node_index": 0,
      "target_node_index": 1
    }
  ]
}

Response:

{
  "success": true,
  "message": "Workflow created successfully",
  "data": {
    "id": "uuid",
    "name": "Order Confirmation",
    ...
  }
}

Get Workflow

GET /api/v1/workflows/{id}

Response:

{
  "success": true,
  "data": {
    "id": "uuid",
    "name": "Order Confirmation",
    "description": "...",
    "status": "active",
    "trigger_type": "event",
    "nodes": [
      {
        "id": "node-uuid",
        "workflow_id": "uuid",
        "node_type": "orders:order-created",
        "node_category": "trigger",
        "label": "Order Created",
        "config": {},
        "position": { "x": 100, "y": 100 },
        "is_entry_point": true,
        "retry_count": 0,
        "retry_delay_seconds": 0,
        "timeout_seconds": 0
      }
    ],
    "connections": [
      {
        "id": "conn-uuid",
        "workflow_id": "uuid",
        "source_node_id": "node-1-uuid",
        "target_node_id": "node-2-uuid",
        "source_handle": "output",
        "target_handle": "input",
        "condition_path": null
      }
    ]
  }
}

Update Workflow

PUT /api/v1/workflows/{id}

Request Body:

{
  "name": "Updated Name",
  "description": "Updated description",
  "status": "active",
  "nodes": [...],
  "connections": [...]
}

Response:

{
  "success": true,
  "message": "Workflow updated successfully",
  "data": { ... }
}

Delete Workflow

DELETE /api/v1/workflows/{id}

Response:

{
  "success": true,
  "message": "Workflow deleted successfully"
}

Activate Workflow

POST /api/v1/workflows/{id}/activate

Validates and activates a workflow. Returns validation errors if invalid.

Response (Success):

{
  "success": true,
  "message": "Workflow activated successfully",
  "data": { ... }
}

Response (Validation Failed):

{
  "success": false,
  "message": "Workflow validation failed",
  "errors": [
    "Workflow must have at least one trigger node",
    "Node 'Send Email' has no incoming connection"
  ]
}

Deactivate Workflow

POST /api/v1/workflows/{id}/deactivate

Pauses a workflow, preventing automatic execution.

Response:

{
  "success": true,
  "message": "Workflow paused successfully",
  "data": { ... }
}

Duplicate Workflow

POST /api/v1/workflows/{id}/duplicate

Request Body:

{
  "name": "Order Confirmation (Copy)"
}

Response:

{
  "success": true,
  "message": "Workflow duplicated successfully",
  "data": {
    "id": "new-uuid",
    "name": "Order Confirmation (Copy)",
    "status": "draft",
    ...
  }
}

Execute Workflow

POST /api/v1/workflows/{id}/execute

Manually triggers workflow execution.

Request Body:

{
  "data": {
    "order_id": "123",
    "customer_email": "john@example.com"
  }
}

Response:

{
  "success": true,
  "message": "Workflow execution started",
  "data": {
    "execution_id": "exec-uuid",
    "status": "pending"
  }
}

Test Run Workflow

POST /api/v1/workflows/{id}/test

Executes workflow in test mode. Validates first.

Request Body:

{
  "data": {
    "sample": "data"
  }
}

Response:

{
  "success": true,
  "message": "Test run completed",
  "data": {
    "execution_id": "exec-uuid",
    "status": "completed",
    "result": { ... }
  }
}

Validate Workflow

GET /api/v1/workflows/{id}/validate

Response:

{
  "success": true,
  "valid": true,
  "errors": []
}

Or with errors:

{
  "success": false,
  "valid": false,
  "errors": [
    "Workflow must have at least one trigger node",
    "Node 'HTTP Request' is missing required config: url"
  ]
}

Export Workflow

GET /api/v1/workflows/{id}/export

Response:

{
  "success": true,
  "data": {
    "name": "Order Confirmation",
    "description": "...",
    "trigger_type": "event",
    "nodes": [...],
    "connections": [...],
    "exported_at": "2025-01-16T10:00:00Z",
    "version": "1.0"
  }
}

Import Workflow

POST /api/v1/workflows/import

Request Body:

{
  "workflow": {
    "name": "Imported Workflow",
    "nodes": [...],
    "connections": [...]
  }
}

Response:

{
  "success": true,
  "message": "Workflow imported successfully",
  "data": { ... }
}

Get Statistics

GET /api/v1/workflows/stats

Response:

{
  "success": true,
  "data": {
    "total_workflows": 25,
    "active_workflows": 18,
    "paused_workflows": 5,
    "draft_workflows": 2,
    "executions_today": 150,
    "executions_this_week": 1050,
    "executions_this_month": 4200,
    "success_rate": 96.5,
    "average_duration_ms": 2340,
    "most_executed": [
      { "id": "uuid", "name": "Order Confirmation", "count": 500 }
    ],
    "recent_failures": [
      { "id": "exec-uuid", "workflow_name": "...", "error": "..." }
    ]
  }
}

Workflow Nodes (Registry)

List All Nodes

GET /api/v1/workflow-nodes

Response:

{
  "success": true,
  "data": [
    {
      "identifier": "workflows:manual-trigger",
      "name": "Manual Trigger",
      "category": "trigger",
      "description": "Start workflow manually",
      "icon": "Play",
      "color": "#22C55E",
      "module": "workflows",
      "inputs": [],
      "outputs": [
        { "name": "output", "label": "Output", "type": "any" }
      ],
      "configSchema": { ... },
      "defaultConfig": { ... }
    },
    ...
  ]
}

Get Nodes Grouped by Category

GET /api/v1/workflow-nodes/grouped

Response:

{
  "success": true,
  "data": [
    {
      "category": "trigger",
      "label": "Triggers",
      "icon": "Zap",
      "color": "#22C55E",
      "description": "Start your workflow with these events",
      "nodes": [
        { "identifier": "workflows:manual-trigger", ... },
        { "identifier": "workflows:schedule-trigger", ... },
        { "identifier": "orders:order-created", ... }
      ]
    },
    {
      "category": "action",
      "label": "Actions",
      "icon": "Play",
      "color": "#3B82F6",
      "nodes": [...]
    }
  ]
}

Get Categories

GET /api/v1/workflow-nodes/categories

Response:

{
  "success": true,
  "data": {
    "trigger": {
      "label": "Triggers",
      "icon": "Zap",
      "color": "#22C55E",
      "description": "Start your workflow with these events"
    },
    "action": { ... },
    "condition": { ... },
    "transformer": { ... }
  }
}

Get Single Node

GET /api/v1/workflow-nodes/{identifier}

Example: GET /api/v1/workflow-nodes/workflows:send-email

Response:

{
  "success": true,
  "data": {
    "identifier": "workflows:send-email",
    "name": "Send Email",
    "category": "action",
    "description": "Send an email to recipients",
    "icon": "Mail",
    "color": "#3B82F6",
    "module": "workflows",
    "inputs": [
      { "name": "input", "label": "Input", "type": "any", "required": true }
    ],
    "outputs": [
      { "name": "output", "label": "Output", "type": "object" }
    ],
    "configSchema": {
      "type": "object",
      "properties": {
        "to": { "type": "string", "title": "To" },
        "subject": { "type": "string", "title": "Subject" },
        "body": { "type": "string", "title": "Body", "format": "textarea" },
        "bodyType": { "type": "string", "enum": ["text", "html"] }
      },
      "required": ["to", "subject", "body"]
    },
    "defaultConfig": {
      "bodyType": "text"
    }
  }
}

Validate Node Config

POST /api/v1/workflow-nodes/{identifier}/validate

Request Body:

{
  "config": {
    "to": "{{customer.email}}",
    "subject": "Order Confirmation"
  }
}

Response:

{
  "success": false,
  "valid": false,
  "errors": [
    "Body is required"
  ]
}

Check Node Connection

POST /api/v1/workflow-nodes/can-connect

Request Body:

{
  "source": "workflows:send-email",
  "target": "workflows:if-else"
}

Response:

{
  "success": true,
  "can_connect": true
}

Simulate a Workflow

POST /api/v1/workflows/{id}/simulate

Walks the workflow and reports what would happen. Nothing is persisted and no step with real-world effects is run — distinct from POST /workflows/{id}/test, which executes for real and does send the email.

Body

{ "data": { "order_id": 42 } }

data is the sample payload the trigger would supply. Optional.

Response200 when the workflow could run, 422 when it could not.

{
  "success": true,
  "data": {
    "ok": true,
    "errors": [],
    "warnings": ["Call: URL is required."],
    "steps": [
      {
        "index": 1,
        "label": "Start",
        "node_type": "workflows:manual-trigger",
        "input": { "order_id": 42 },
        "config": {},
        "mode": "executed",
        "reason": null
      },
      {
        "index": 2,
        "label": "Email",
        "node_type": "workflows:send-email",
        "mode": "stubbed",
        "reason": "Not run — this step has real-world effects."
      }
    ],
    "unreached": ["Stranded"],
    "variables": { "greeting": "hello" },
    "executed": 1,
    "stubbed": 1
  }
}

unreached lists nodes the run would never arrive at — usually something left unconnected on the canvas.

Executions

List Executions

GET /api/v1/workflow-executions

Query Parameters:

Parameter Type Description
workflow_id uuid Filter by workflow
status string Filter by status
trigger_type string Filter by trigger type
from date From date
to date To date
per_page integer Items per page

Response:

{
  "success": true,
  "data": [
    {
      "id": "exec-uuid",
      "workflow_id": "uuid",
      "workflow_name": "Order Confirmation",
      "status": "completed",
      "trigger_type": "event",
      "trigger_data": { ... },
      "triggered_by": "user-uuid",
      "result": { ... },
      "error_message": null,
      "nodes_total": 5,
      "nodes_executed": 5,
      "nodes_failed": 0,
      "started_at": "2025-01-16T10:00:00Z",
      "completed_at": "2025-01-16T10:00:02Z",
      "duration_ms": 2340
    }
  ],
  "meta": { ... }
}

Get Workflow Executions

GET /api/v1/workflows/{workflow}/executions

Returns executions for a specific workflow.


Get Execution Detail

GET /api/v1/workflow-executions/{execution}

Response:

{
  "success": true,
  "data": {
    "id": "exec-uuid",
    "workflow": { ... },
    "status": "completed",
    "trigger_type": "manual",
    "trigger_data": { "order_id": "123" },
    "context": {
      "variables": { "discount": 10 }
    },
    "result": { ... },
    "nodes_total": 5,
    "nodes_executed": 5,
    "started_at": "...",
    "completed_at": "...",
    "resume_at": null,
    "logs": [
      {
        "id": "log-uuid",
        "node_id": "node-uuid",
        "node_type": "workflows:manual-trigger",
        "node_label": "Manual Trigger",
        "status": "completed",
        "input_data": { ... },
        "output_data": { ... },
        "started_at": "...",
        "completed_at": "...",
        "duration_ms": 5
      }
    ]
  }
}

resume_at is set only while a run is suspended waiting out a Delay node or a retry backoff — it is when the sweeper may wake it. A run an operator paused has resume_at: null and stays put until it is explicitly re-dispatched.

The scheduler snapshot backing a suspended run is internal and is not serialized on any endpoint.


Get Execution Logs

GET /api/v1/workflow-executions/{execution}/logs

Response:

{
  "success": true,
  "data": [
    {
      "id": "log-uuid",
      "node_id": "node-uuid",
      "node_type": "workflows:send-email",
      "node_label": "Send Confirmation",
      "status": "completed",
      "input_data": {
        "order_id": "123",
        "customer_email": "john@example.com"
      },
      "output_data": {
        "order_id": "123",
        "customer_email": "john@example.com",
        "_email_sent": true,
        "_sent_at": "2025-01-16T10:00:01Z"
      },
      "error_message": null,
      "retry_attempt": 0,
      "started_at": "2025-01-16T10:00:00.500Z",
      "completed_at": "2025-01-16T10:00:01.234Z",
      "duration_ms": 734
    }
  ]
}

Cancel Execution

POST /api/v1/workflow-executions/{execution}/cancel

Only works for pending or running executions.

Response:

{
  "success": true,
  "message": "Execution cancelled"
}

Pause Execution

POST /api/v1/workflow-executions/{execution}/pause

Asks a running execution to suspend itself. The worker is not interrupted — it finishes the node it is on, saves a checkpoint and stops there, so this returns before the run has come to rest.

The execution keeps its position and no due time is set, so it stays paused until re-dispatched. Returns 400 if the execution has already finished or is already paused.

Response:

{
  "success": true,
  "message": "Execution will pause once the current step finishes"
}

Retry Execution

POST /api/v1/workflow-executions/{execution}/retry

Creates a new execution with the same trigger data.

Response:

{
  "success": true,
  "message": "Execution retry started",
  "data": {
    "execution_id": "new-exec-uuid"
  }
}

Webhooks

Get Webhook URL

GET /api/v1/workflows/{workflow}/webhook-url

Response:

{
  "success": true,
  "data": {
    "url": "https://your-domain.com/api/webhooks/workflows/abc123token",
    "method": "POST",
    "token": "abc123token"
  }
}

Trigger Webhook (Public)

POST /api/webhooks/workflows/{token}

No authentication required - uses token for identification.

Request Body:

{
  "event": "order.created",
  "data": {
    "order_id": "123"
  }
}

Headers (if signature required):

X-Webhook-Signature: sha256=abc123...

Response:

{
  "success": true,
  "message": "Workflow triggered",
  "data": {
    "execution_id": "exec-uuid"
  }
}

Error Responses

All endpoints may return these error responses:

400 Bad Request

{
  "success": false,
  "message": "Invalid request",
  "errors": { ... }
}

401 Unauthorized

{
  "success": false,
  "message": "Unauthenticated"
}

403 Forbidden

{
  "success": false,
  "message": "Insufficient permissions"
}

404 Not Found

{
  "success": false,
  "message": "Workflow not found"
}

422 Validation Failed

{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "name": ["The name field is required"]
  }
}

500 Server Error

{
  "success": false,
  "message": "An error occurred",
  "error": "Error details (in debug mode)"
}

Rate Limiting

API endpoints are rate limited:

  • Standard endpoints: 60 requests/minute
  • Webhook endpoints: 120 requests/minute
  • Execute endpoints: 30 requests/minute

Rate limit headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1705398000

Next Steps