This document captures the complete and final set of requirements for the Workflow Engine project.
Build a full-stack workflow automation application (similar in spirit to Zapier) that allows users to:
- Define workflows as JSON
- Trigger workflows via unique HTTP endpoints
- Execute workflow steps sequentially
- Integrate with external systems such as Slack or Discord via HTTP webhooks
- Node.js
- TypeScript
- HTTP framework of choice (Express is acceptable)
- React
- TypeScript
- Can use any framework (Next.js is acceptable)
- Relational database (PostgreSQL, MySQL, etc.)
- The application is single-tenant
- No authentication or user identity
- All workflows are globally visible and editable
Each workflow must have:
id(generated by backend)name(string)enabled(boolean)trigger(exactly one HTTP trigger)steps(ordered list, must not be empty)
- Only HTTP triggers are supported
- Backend generates a unique, publicly accessible URL path for each workflow
- Triggered via HTTP
POST
Example:
{
"type": "http",
"path": "/t/<random-id>"
}The backend must expose the following endpoints:
POST /workflows– create a workflowGET /workflows– list all workflowsGET /workflows/:id– fetch a single workflowPUT /workflows/:idorPATCH /workflows/:id– update a workflowDELETE /workflows/:id– delete a workflow
- Workflow must contain at least one step
- Unsupported step types must result in a validation error
- Unsupported transform operations must result in a validation error
- Invalid workflow payloads must return clear 4xx errors
- A
POSTrequest to the workflow’s trigger URL starts a workflow run - If the workflow is disabled, the trigger endpoint must return 403 or 404
- Each trigger request creates a new workflow run
- No idempotency or “exactly once” guarantees are required
- The trigger endpoint must return a synchronous HTTP response
- The response must include the workflow run status:
successskippedfailed
- Steps execute sequentially, in the order defined
- Execution stops on:
- First failed step →
failed - Failed filter condition →
skipped
- First failed step →
A workflow run record must be created when execution starts and updated when execution completes.
Each workflow run must persist:
- Workflow ID
- Status (
success,skipped,failed) - Start timestamp
- End timestamp
- Error details (if any)
For failed HTTP request steps, additionally persist:
- HTTP status code
- Response headers
- Response body
Valid statuses are:
success– all steps executed successfullyskipped– execution stopped by a filter stepfailed– execution stopped due to an error
ctxis initialized using the inbound HTTP request bodyctxis passed to each step- Steps may read and/or mutate
ctx - Missing fields accessed via dot-paths resolve to
null
Purpose: Gate execution based on values in ctx.
Shape:
{
"type": "filter",
"conditions": [{ "path": "field", "op": "eq", "value": "x" }]
}Rules:
-
Supported ops:
eq,neq -
Operates on
ctxusing dot-paths -
All conditions must pass
-
If any condition fails:
- Execution stops
- Run status =
skipped
-
Filter steps do not modify
ctx
Purpose: Modify ctx for downstream steps.
Supported operations:
defaulttemplatepick
Rules:
- Transform steps only modify
ctx - No side effects
- Operations execute in order
- Dot-paths behave similar to
lodash.get/set - Template operations replace missing values with an empty string
pick operation:
- Retains only the specified fields
- Removes all other fields from
ctx - Can operate on root or nested objects
Example:
{
"type": "transform",
"ops": [
{ "op": "default", "path": "actor_name", "value": "Unknown" },
{ "op": "template", "to": "title", "template": "Event {{type}}" }
]
}Purpose: Call external systems.
Shape:
{
"type": "http_request",
"method": "POST",
"url": "https://example.com/webhook",
"headers": { "Content-Type": "application/json" },
"body": { "mode": "custom", "value": { "text": "{{title}}" } },
"timeoutMs": 2000,
"retries": 3
}Rules:
-
HTTP retries are handled synchronously
-
Retries must occur on:
- Network errors
- HTTP 5xx responses
-
No need to support chaining or dependency handling between HTTP steps
-
Supported body modes:
ctx: send the entirectxas JSONcustom: send a custom JSON object with templates
On failure:
- Stop execution
- Mark run as
failed - Persist HTTP failure details
The frontend must support:
- List workflows
- Create workflows
- Edit workflows
- Delete workflows
- View a workflow’s generated trigger URL
- Edit workflow steps as raw JSON using a code editor (e.g., Monaco)
UI design is not graded, but the UI must be usable.
The system must demonstrate:
- Workflow creation
- Triggering via HTTP
- Sequential execution of steps
- Posting a message to Slack or Discord via webhook