LoopFour

Workflows API

Create, manage, and execute workflow definitions

Workflows API

Manage workflow definitions including creation, updates, versioning, and execution.

List Workflows

Retrieve all workflows for your company with cursor-based pagination.

GET /api/v1/workflows

Query Parameters

ParameterTypeDefaultDescription
statusstring-Filter by status: draft, active, paused, archived
searchstring-Search by workflow name
limitnumber50Number of results (1-100)
cursorstring-Cursor for pagination

Response

{
  "success": true,
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Invoice Automation",
      "description": "Process incoming invoices",
      "status": "active",
      "version": 3,
      "trigger": {
        "type": "webhook",
        "provider": "stripe",
        "events": ["invoice.paid"]
      },
      "createdAt": "2024-01-15T10:00:00.000Z",
      "updatedAt": "2024-01-16T14:30:00.000Z"
    }
  ],
  "meta": {
    "cursor": "eyJ0cyI6MTcwNTMxMjAwMDAwMCwiaWQiOiI1NTBlODQwMC4uLiJ9",
    "hasMore": true
  }
}

Example

curl -X GET "http://localhost:3000/api/v1/workflows?status=active&limit=10" \
  -H "x-api-key: YOUR_API_KEY"

Create Workflow

Create a new workflow definition. Workflows are created in draft status.

POST /api/v1/workflows

Request Body

FieldTypeRequiredDescription
namestringYesWorkflow name (1-255 chars)
descriptionstringNoHuman-readable description
triggerobjectYesTrigger configuration
stepsarrayYesArray of workflow steps
inputSchemaobjectNoJSON Schema for input validation
outputSchemaobjectNoJSON Schema for output
settingsobjectNoWorkflow settings

Example Request

{
  "name": "New Customer Onboarding",
  "description": "Send welcome email and create CRM contact",
  "trigger": {
    "type": "webhook",
    "provider": "stripe",
    "events": ["customer.created"]
  },
  "steps": [
    {
      "id": "send-welcome",
      "type": "email",
      "name": "Send Welcome Email",
      "config": {
        "action": "send",
        "to": "{{input.data.object.email}}",
        "subject": "Welcome to our platform!",
        "body": {
          "html": "<p>Hello {{input.data.object.name}},</p><p>Welcome aboard!</p>"
        }
      }
    },
    {
      "id": "create-contact",
      "type": "action",
      "name": "Create HubSpot Contact",
      "action": "hubspot.createContact",
      "config": {
        "email": "{{input.data.object.email}}",
        "properties": {
          "firstname": "{{input.data.object.name}}",
          "stripe_customer_id": "{{input.data.object.id}}"
        }
      }
    }
  ],
  "inputSchema": {
    "type": "object",
    "required": ["data"],
    "properties": {
      "data": {
        "type": "object",
        "properties": {
          "object": {
            "type": "object",
            "properties": {
              "id": { "type": "string" },
              "email": { "type": "string" },
              "name": { "type": "string" }
            }
          }
        }
      }
    }
  }
}

Response (201 Created)

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "New Customer Onboarding",
    "description": "Send welcome email and create CRM contact",
    "status": "draft",
    "version": 1,
    "trigger": { ... },
    "steps": [ ... ],
    "inputSchema": { ... },
    "outputSchema": null,
    "settings": null,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T10:00:00.000Z"
  }
}

Get Workflow

Retrieve a specific workflow by ID.

GET /api/v1/workflows/:id

Path Parameters

ParameterTypeDescription
idstringWorkflow UUID

Response

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Invoice Automation",
    "description": "Process incoming invoices",
    "status": "active",
    "version": 3,
    "trigger": {
      "type": "webhook",
      "provider": "stripe",
      "events": ["invoice.paid"]
    },
    "steps": [
      {
        "id": "notify-team",
        "type": "action",
        "name": "Notify Slack",
        "action": "slack.sendMessage",
        "config": {
          "channel": "#finance",
          "text": "Invoice {{input.data.object.number}} paid!"
        }
      }
    ],
    "inputSchema": null,
    "outputSchema": null,
    "settings": null,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-16T14:30:00.000Z"
  }
}

Update Workflow

Update a workflow. If the workflow is active, this increments the version and saves the previous version to history.

PUT /api/v1/workflows/:id

Request Body

FieldTypeRequiredDescription
namestringNoUpdated workflow name
descriptionstringNoUpdated description
triggerobjectNoUpdated trigger configuration
stepsarrayNoUpdated steps array
inputSchemaobjectNoUpdated input schema
outputSchemaobjectNoUpdated output schema
settingsobjectNoUpdated settings
changeSummarystringNoDescription of changes (stored in version history)

Example Request

{
  "name": "Invoice Automation v2",
  "steps": [
    {
      "id": "notify-team",
      "type": "action",
      "name": "Notify Slack",
      "action": "slack.sendMessage",
      "config": {
        "channel": "#finance",
        "text": "Invoice {{input.data.object.number}} paid for ${{input.data.object.amount_paid / 100}}!"
      }
    }
  ],
  "changeSummary": "Added amount to Slack notification"
}

Response

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Invoice Automation v2",
    "status": "active",
    "version": 4,
    ...
  }
}

Delete Workflow

Soft delete (archive) a workflow. This also deletes any associated schedules.

DELETE /api/v1/workflows/:id

Response

{
  "success": true,
  "data": {
    "deleted": true
  }
}

Activate Workflow

Change workflow status to active. This enables the workflow to be triggered.

POST /api/v1/workflows/:id/activate

Status Transitions

FromTo Active
draftAllowed
pausedAllowed
activeNot allowed (already active)
archivedNot allowed

Response

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "active"
  }
}

For schedule triggers, the response includes schedule information:

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "active",
    "schedule": {
      "id": "sch_xxx",
      "triggerScheduleId": "sched_xxx",
      "nextRunAt": "2024-01-16T09:00:00.000Z"
    }
  }
}

Pause Workflow

Pause an active workflow. Paused workflows won't be triggered.

POST /api/v1/workflows/:id/pause

Response

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "paused"
  }
}

Run Workflow

Trigger a workflow execution via the API. Supports both async (default) and sync execution modes.

POST /api/v1/workflows/:id/run

Request Body

FieldTypeRequiredDescription
inputobjectNoInput data for the workflow
connectionIdstringNoSpecific connection to use
idempotencyKeystringNoPrevent duplicate runs (24h TTL)
modestringNoasync (default) or sync
maxExecutionTimeMsnumberNoMax wait time for sync mode (1000-300000ms)
onTimeoutstringNoSync timeout behavior: return_run_id, throw_error
onFailurestringNoSync failure behavior: throw_error, switch_to_async

Async Mode (Default)

Returns immediately with a run ID. Poll the runs API for status updates.

{
  "input": {
    "customerId": "cus_xxx",
    "amount": 9900
  }
}

Response (202 Accepted):

{
  "success": true,
  "data": {
    "run_id": "run_550e8400-e29b-41d4-a716-446655440000",
    "status": "pending",
    "workflow_id": "550e8400-e29b-41d4-a716-446655440000",
    "mode": "async"
  }
}

Sync Mode

Waits for workflow completion and returns the result directly. Ideal for short-running workflows in request/response scenarios.

{
  "input": {
    "customerId": "cus_xxx",
    "amount": 9900
  },
  "mode": "sync",
  "maxExecutionTimeMs": 30000,
  "onTimeout": "return_run_id",
  "onFailure": "throw_error"
}

Sync Success Response (200):

{
  "success": true,
  "data": {
    "run_id": "run_550e8400-e29b-41d4-a716-446655440000",
    "status": "completed",
    "workflow_id": "550e8400-e29b-41d4-a716-446655440000",
    "mode": "sync",
    "output": {
      "invoiceId": "inv_xxx",
      "status": "sent"
    },
    "duration_ms": 2500
  }
}

Sync Timeout Response (202):

When execution exceeds maxExecutionTimeMs with onTimeout: "return_run_id":

{
  "success": true,
  "data": {
    "run_id": "run_550e8400-e29b-41d4-a716-446655440000",
    "status": "running",
    "workflow_id": "550e8400-e29b-41d4-a716-446655440000",
    "mode": "async",
    "timed_out": true,
    "message": "Execution in progress. Use GET /runs/:id to poll for results."
  }
}

Sync Mode Parameters

ParameterDefaultDescription
maxExecutionTimeMs30000Max wait time (1 second to 5 minutes)
onTimeoutreturn_run_idreturn_run_id returns gracefully, throw_error returns 408
onFailurethrow_errorthrow_error returns error, switch_to_async returns run ID

Sync mode is best for workflows completing under 30 seconds. For longer workflows, use async mode with polling or webhooks.

Idempotency

If the same idempotencyKey is used within 24 hours, the existing run is returned:

{
  "success": true,
  "data": {
    "run_id": "run_existing",
    "status": "completed",
    "workflow_id": "550e8400-e29b-41d4-a716-446655440000",
    "message": "Existing run returned (idempotency key matched)"
  }
}

List Workflow Runs

Get runs for a specific workflow.

GET /api/v1/workflows/:id/runs

Query Parameters

ParameterTypeDefaultDescription
statusstring-Filter: pending, running, completed, failed, cancelled
limitnumber50Number of results (1-100)
cursorstring-Cursor for pagination

Response

{
  "success": true,
  "data": [
    {
      "id": "run_xxx",
      "workflowId": "550e8400-e29b-41d4-a716-446655440000",
      "status": "completed",
      "triggerType": "api",
      "startedAt": "2024-01-15T10:30:00.000Z",
      "completedAt": "2024-01-15T10:30:05.000Z",
      "durationMs": 5000,
      "error": null,
      "createdAt": "2024-01-15T10:30:00.000Z"
    }
  ],
  "meta": {
    "cursor": "eyJ0cyI6MTcwNTMxMjAwMDAwMCwiaWQiOiJydW5feHh4In0=",
    "hasMore": false
  }
}

Clone Workflow

Create a copy of an existing workflow.

POST /api/v1/workflows/:id/clone

Request Body

FieldTypeRequiredDescription
namestringYesName for the cloned workflow

Example Request

{
  "name": "Invoice Automation (Copy)"
}

Response (201 Created)

{
  "success": true,
  "data": {
    "id": "new-workflow-id",
    "name": "Invoice Automation (Copy)",
    "status": "draft",
    "version": 1,
    "clonedFrom": "550e8400-e29b-41d4-a716-446655440000",
    ...
  }
}

Version Management

List Versions

Get all versions of a workflow.

GET /api/v1/workflows/:id/versions

Response

{
  "success": true,
  "data": {
    "workflowId": "550e8400-e29b-41d4-a716-446655440000",
    "currentVersion": 4,
    "versions": [
      {
        "version": 4,
        "status": "current",
        "isCurrent": true
      },
      {
        "version": 3,
        "name": "Invoice Automation",
        "changeSummary": "Added error handling",
        "status": "archived",
        "createdAt": "2024-01-15T10:00:00.000Z",
        "isCurrent": false
      },
      {
        "version": 2,
        "name": "Invoice Automation",
        "changeSummary": "Added Slack notification",
        "status": "archived",
        "createdAt": "2024-01-14T10:00:00.000Z",
        "isCurrent": false
      }
    ]
  }
}

Get Specific Version

GET /api/v1/workflows/:id/versions/:version

Response

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "version": 2,
    "name": "Invoice Automation",
    "description": "Process incoming invoices",
    "trigger": { ... },
    "steps": [ ... ],
    "changeSummary": "Added Slack notification",
    "status": "archived",
    "isCurrent": false,
    "createdAt": "2024-01-14T10:00:00.000Z"
  }
}

Compare Versions

Compare changes between two versions.

GET /api/v1/workflows/:id/versions/compare?from=2&to=4

Query Parameters

ParameterTypeRequiredDescription
fromnumberYesSource version number
tonumberYesTarget version number

Response

{
  "success": true,
  "data": {
    "fromVersion": 2,
    "toVersion": 4,
    "changes": {
      "name": {
        "from": "Invoice Automation",
        "to": "Invoice Automation v2",
        "changed": true
      },
      "description": {
        "from": "Process incoming invoices",
        "to": "Process incoming invoices",
        "changed": false
      },
      "trigger": {
        "changed": false
      },
      "steps": {
        "added": ["error-handler"],
        "removed": [],
        "modified": ["notify-team"]
      }
    }
  }
}

Rollback Version

Restore a workflow to a previous version. This creates a new version with the old content.

POST /api/v1/workflows/:id/versions/:version/rollback

Request Body

FieldTypeRequiredDescription
changeSummarystringNoReason for rollback

Example Request

{
  "changeSummary": "Rolling back due to bug in v4"
}

Response

{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "version": 5,
    "rolledBackFrom": 4,
    "rolledBackTo": 2,
    "name": "Invoice Automation",
    "status": "active",
    ...
  }
}

Schedule Management

Get Schedule

Get schedule information for a workflow with schedule trigger.

GET /api/v1/workflows/:id/schedule

Response

{
  "success": true,
  "data": {
    "id": "sch_xxx",
    "workflowId": "550e8400-e29b-41d4-a716-446655440000",
    "triggerScheduleId": "sched_xxx",
    "scheduleType": "cron",
    "cronExpression": "0 9 * * 1-5",
    "interval": null,
    "timezone": "America/New_York",
    "enabled": true,
    "nextRunAt": "2024-01-16T09:00:00.000Z",
    "lastRunAt": "2024-01-15T09:00:00.000Z",
    "lastRunId": "run_xxx",
    "runCount": 42,
    "consecutiveFailures": 0,
    "lastError": null,
    "createdAt": "2024-01-01T10:00:00.000Z",
    "updatedAt": "2024-01-15T09:00:05.000Z"
  }
}

Update Schedule

Modify schedule configuration.

PUT /api/v1/workflows/:id/schedule

Request Body

FieldTypeRequiredDescription
cronstringNoCron expression
intervalobjectNoInterval configuration
interval.valuenumberYesInterval value
interval.unitstringYesminutes, hours, days, weeks
timezonestringNoIANA timezone
enabledbooleanNoEnable/disable schedule

Example Request

{
  "cron": "0 10 * * 1-5",
  "timezone": "America/Los_Angeles",
  "enabled": true
}

Response

{
  "success": true,
  "data": {
    "updated": true,
    "scheduleId": "sch_xxx",
    "nextRunAt": "2024-01-16T10:00:00.000Z"
  }
}

Manual Schedule Trigger

Manually trigger a scheduled workflow (for testing).

POST /api/v1/workflows/:id/schedule/trigger

Response (202 Accepted)

{
  "success": true,
  "data": {
    "runId": "run_xxx",
    "status": "pending",
    "workflowId": "550e8400-e29b-41d4-a716-446655440000",
    "triggeredAt": "2024-01-15T10:30:00.000Z"
  }
}

Workflow Statuses

StatusDescription
draftInitial state, not yet activated
activeRunning and can be triggered
pausedTemporarily disabled
archivedSoft deleted

Error Responses

Workflow Not Found (404)

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Workflow not found"
  }
}

Invalid Status Transition (400)

{
  "success": false,
  "error": {
    "code": "BAD_REQUEST",
    "message": "Cannot transition from 'archived' to 'active'",
    "details": {
      "currentStatus": "archived",
      "targetStatus": "active",
      "allowedTransitions": []
    }
  }
}

Validation Error (400)

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid workflow steps: Step 'send-email' references non-existent step 'process-data'"
  }
}

Workflow Not Active (400)

{
  "success": false,
  "error": {
    "code": "WORKFLOW_NOT_ACTIVE",
    "message": "Workflow must be active to run"
  }
}