Overview
FlowForge is a workflow automation platform that runs in your own AWS account. You build workflows by connecting nodes on a canvas. A workflow starts from a trigger, such as a webhook, a schedule, an API call or an AI agent, and passes data from node to node: calling APIs, querying databases, posting to chat, asking Claude to summarise or classify, and branching on the results.
A FlowForge installation has two parts that share one MySQL database:
- The web application: the visual editor, workspaces, users, credentials, API keys, webhook endpoints, the REST API and the MCP server.
- The engine: a background service (
flowforge-engine) that picks up queued runs, executes each workflow node by node, fires schedules and records every step.
Your workflows, credentials and run history stay in your AWS account. Credentials are encrypted at rest and are only decrypted inside the engine while a workflow runs.
Deploy from AWS Marketplace
FlowForge is delivered as an Amazon Machine Image (AMI) with a CloudFormation template. The template creates the EC2 instance, its security groups and IAM role, and optionally an RDS database and an Application Load Balancer.
Choose a database mode
- local: MySQL 8.4 runs on the instance with randomly generated passwords. Good for evaluation and small teams.
- rds: Amazon RDS for MySQL, with the database password stored in AWS Secrets
Manager. Recommended for production; turn on
DBMultiAZfor high availability.
Launch the stack
- Subscribe to FlowForge on AWS Marketplace and choose the CloudFormation delivery option.
- Pick your region and launch the stack.
- Fill in the parameters below, then create the stack. It typically takes a few minutes; RDS adds several more.
- When the stack is complete, open the Outputs tab.
| Parameter | Default | What it does |
|---|---|---|
InstanceType |
t3.medium | EC2 instance size: t3.small to t3.xlarge, m6i.large/xlarge or m7i.large/xlarge. |
KeyName |
(empty) | Optional EC2 key pair for SSH. Session Manager works without one. |
RootVolumeSize |
30 | Root disk size in GB. |
VpcId |
The VPC to launch into. | |
Subnets |
The instance goes in the first subnet. RDS and the load balancer need two subnets in different Availability Zones. | |
AccessCidr |
0.0.0.0/0 | Who can reach the web interface and webhooks. Narrow this if only your network needs access. |
SshCidr |
(empty) | Who can reach SSH. Leave empty to keep port 22 closed. |
DatabaseMode |
local | local: MySQL 8.4 on the instance. rds: Amazon RDS for MySQL with its password in Secrets Manager (recommended for production). |
DBInstanceClass |
db.t4g.medium | RDS instance size (rds mode only). |
DBAllocatedStorage |
20 | RDS storage in GB (rds mode only). |
DBMultiAZ |
false | Run RDS across two Availability Zones (rds mode only). |
CertificateArn |
(empty) | Optional ACM certificate. When set, an Application Load Balancer terminates HTTPS in front of the instance. |
Stack outputs
| Output | Use it for |
|---|---|
SetupUrl |
The address of the setup wizard. Without a certificate, the instance uses a self-signed certificate, so accept the browser warning and install a real certificate later. With a load balancer, point your domain at it so the certificate matches. |
SetupToken |
How to prove you own the instance: the EC2 instance ID, or the token file read through Session Manager. |
InstanceId |
The EC2 instance running FlowForge. |
DatabaseEndpoint |
The RDS endpoint (rds mode only). |
Using a load balancer? Set TRUSTED_PROXIES=* in
/var/www/automation/web/.env so FlowForge detects HTTPS and client IP addresses
correctly. See Administration.
First-time setup
Until setup is finished, every page redirects to the setup wizard. Open the SetupUrl from
the stack outputs and work through five steps:
| Step | What happens |
|---|---|
| 1. Verify server | Checks the server requirements and asks you to prove you control the instance. Enter the EC2 instance ID, or the setup token. A new instance is reachable by anyone, so this step stops someone else from claiming it. |
| 2. Database | Already filled in with the database prepared when the instance first booted. The connection is tested live. |
| 3. Application | Public URL, application name, outgoing email (log only, or SMTP such as Amazon SES), and whether workflows may reach private network addresses. |
| 4. Company & admin | Your company name and the first administrator. This person owns the company workspace and is a platform administrator. On AWS Marketplace launches the registration key is optional. |
| 5. Install | Writes the configuration, creates the database tables, the workspace and the administrator, then registers the installation. |
To read the setup token without SSH, start a Session Manager session and print the token file:
aws ssm start-session --target <instance-id>
sudo cat /var/www/automation/web/storage/app/setup-token
Registration sends your company name, the administrator's name and email, the application URL and version, and the instance's signed AWS identity document to Str8in. Passwords are never sent. If registration fails, your installation is kept and the wizard shows a Retry registration page. After registration succeeds, the wizard is locked permanently.
Workspaces, users and roles
Everything in FlowForge (workflows, credentials, API keys, webhooks, data store values and run history) belongs to a workspace. Workspaces are fully isolated from each other. A person can belong to several workspaces and switch between them from the sidebar.
| Role | Can |
|---|---|
| Viewer | See workflows, executions and webhook endpoints. |
| Editor | Everything a viewer can, plus create, import, edit, run, activate, duplicate and delete workflows; cancel and retry executions; and manage credentials. |
| Admin | Everything an editor can, plus manage members, API keys and workspace settings. |
| Owner | Everything an admin can, plus grant the owner role. |
Admins add people under Members. Anyone signed in can create an additional workspace from Workspaces → New.
Platform administrators (the administrator created during setup) also see Platform → All tenants. It lists every workspace with its members, workflow count and usage, and lets you suspend or reactivate a workspace and set its limits. It never shows a workspace's workflows or secrets.
Building workflows
- Go to Workflows and create a workflow.
- Drag nodes from the palette onto the canvas. Connect them by dragging from a node's output dot to the next node's input dot.
- Click a node to configure it. Most fields accept expressions, so you can use data from earlier steps.
- Click ▶ Run to run the workflow now, using the Manual trigger's sample input. Each node shows ✓ or ✕; click a node to see exactly what went in and what came out.
- Switch the workflow to Active to turn on its Webhook, Schedule and MCP tool triggers.
Every run is recorded under Executions with the input, output, timing and any error for each step. From there you can cancel a running execution or retry a failed one. Workflows can be exported to JSON and imported into another workspace or installation.
How data flows
Each node receives one JSON value from the node before it and passes one JSON value on. To process a list item by item, use Split into items.
Most data nodes have Put result in field and Keep incoming fields settings, so you can add a result to the incoming data instead of replacing it.
Handling failures
Every action node has three settings for failures:
- Retries on failure: try again up to 5 times.
- Wait between retries: up to 60 seconds between attempts.
- Continue on failure: record the error and carry on with the rest of the workflow instead of stopping.
Expressions
Wrap an expression in double curly braces to use data from the run in any field:
Hello {{ $json.customer.name }}, your order {{ $trigger.body.order_id }} has shipped.
| Variable | Contains |
|---|---|
$json |
The output of the previous node. |
$trigger |
The data that started the run. For a webhook: body,
query, headers and so on. |
$node["Name"] |
The output of any earlier node, by name, for example
$node["HTTP Request"].json.body. |
$execution |
The run's id and mode. |
$workflow |
The workflow's id and name. |
$now |
The current time as an ISO 8601 string. |
When a field contains only a single expression, such as {{ $json.items }}, the value
keeps its type: a number stays a number and an object stays an object. When the expression is
mixed with other text, the result is text.
Triggers
A workflow can have several triggers. Webhook, Schedule and MCP tool triggers only fire while the workflow is active.
Manual
Runs the workflow from the editor's ▶ Run button or the REST API. Its Sample input is used when you run from the editor.
Webhook
Every workspace has its own webhook namespace. Two workspaces can use the same path without conflict.
{METHOD} https://<your-flowforge>/api/hooks/<workspace-slug>/<path>
- HTTP method: GET, POST, PUT, PATCH or DELETE.
- Path: for example
orders/created. - Authentication: none (public), a header credential, a basic auth credential, or
a workspace API key with the
workflows:executeability. - Respond: immediately with
202and the execution ID, or when the workflow finishes. When waiting, the caller receives the last node's output, or whatever a Respond to webhook node sets. If the workflow takes longer than the wait timeout (25 seconds by default), the caller gets202with the execution ID instead.
The Endpoints page lists every webhook URL in the workspace. Request bodies are limited to 1 MB by default.
Schedule
Runs on a cron expression (minute hour day-of-month month day-of-week) in the timezone you
choose. For example, 0 9 * * 1-5 with timezone America/New_York runs at
9:00 AM Eastern on weekdays.
MCP tool
Publishes the workflow as a tool for AI agents. Give it a tool name (letters, numbers,
_ and -, unique in the workspace), a description that tells
the AI when to use it, and a JSON Schema for its input. Mark it
read-only if it does not change anything. See MCP server.
Node reference
FlowForge includes 28 built-in nodes. For any service without a dedicated node, use HTTP Request with a credential.
| Group | Node | What it does |
|---|---|---|
| Trigger | Manual | Starts a run from the editor or the API, using its sample input. |
| Trigger | Webhook | Starts a run when an HTTP request arrives at the workspace's webhook URL. |
| Trigger | Schedule | Starts a run on a cron schedule in the timezone you choose. |
| Trigger | MCP tool | Publishes the workflow as a tool that AI agents can call through the MCP server. |
| Action | HTTP Request | Calls any REST API, with authentication from a credential (bearer, header, basic, query key, OAuth2 client credentials). |
| Action | Database query | Runs parameterised SQL against MySQL or PostgreSQL and returns up to 1,000 rows. |
| Action | Slack message | Posts to Slack through an incoming webhook. |
| Action | Microsoft Teams message | Posts an Adaptive Card through a Teams Workflows webhook. |
| Action | Send SMS (Twilio) | Sends a text message through your Twilio account. |
| Action | Send email (SMTP) | Sends email through your SMTP server. |
| Action | Claude AI | Summarises, classifies, extracts or drafts text with Claude Opus, Sonnet or Haiku, with optional JSON-schema output. |
| Action | Execute workflow | Runs another workflow in the same workspace as a sub-routine, up to 5 levels deep. |
| Action | Respond to webhook | Sets the status, headers and body returned to a webhook caller that is waiting for the result. |
| Data | Set fields | Adds or overwrites fields on an object. |
| Data | Rename & remove fields | Renames or drops fields on an object. |
| Data | Filter | Keeps the items that match a condition ($item, $index). Outputs: kept and discarded. |
| Data | Split into items | Fans out over an array so each item is processed separately. |
| Data | Item lists | Sort, limit, dedupe, pluck, group, chunk, sum, average, min and max over a list. |
| Data | Date & time | Formats dates, converts timezones, adds or subtracts time, takes differences and rounds to the start of a period. |
| Data | Text | Replace, regex extract, split, change case, slugify, truncate and strip HTML. |
| Data | Convert format | Converts between JSON, CSV, XML and query strings in both directions. |
| Data | Crypto & encoding | Hash, HMAC sign and verify, Base64, UUIDs and random tokens. |
| Data | Data store | Key-value storage for the workspace that persists between runs: get, set, increment, delete, list, with optional expiry. |
| Data | Code (JavaScript) | Runs your JavaScript in a sandbox with no network, filesystem or Node APIs, under memory and time limits. |
| Logic | IF | Routes to a true or false branch. |
| Logic | Switch | Routes to one of several branches. |
| Logic | Stop and error | Ends the run as failed with your message. |
| Utility | Wait | Pauses the run for up to 300 seconds. |
| Utility | No operation | Does nothing. Useful as a placeholder or a junction. |
Database queries go through the same private-network guard as HTTP requests. To reach a database
inside your VPC, allow private networks during setup or set
ENGINE_ALLOW_PRIVATE_NETWORKS=true.
Credentials
Store API keys, passwords and connection details under Credentials, then pick them in a node's settings. Credentials are encrypted at rest, are only decrypted inside the engine while a workflow runs, and are never shown again in the interface or returned by the API.
| Type | Holds | Used by |
|---|---|---|
| Bearer token | Token | HTTP Request |
| HTTP header | Header name and value | HTTP Request, Webhook authentication |
| Basic auth | Username and password | HTTP Request, Webhook authentication |
| API key in query string | Parameter name and value | HTTP Request |
| OAuth2 client credentials | Token URL, client ID, client secret, optional scope | HTTP Request |
| Database (MySQL / PostgreSQL) | Driver, host, port, database, username, password, SSL | Database query |
| Slack incoming webhook | Webhook URL | Slack message |
| Microsoft Teams workflow webhook | Webhook URL | Microsoft Teams message |
| Twilio | Account SID, auth token and default from number | Send SMS |
| Anthropic (Claude) API key | API key | Claude AI |
| Secret value | A secret such as an HMAC key or signing secret | Crypto & encoding |
| SMTP server | Host, port, TLS, username, password and default from address | Send email |
The Claude AI node needs an Anthropic (Claude) API key credential from your own Anthropic account.
REST API
Admins create API keys under API keys. Each key belongs to one workspace, starts with
ffk_, can be limited to specific abilities and can have an expiry date. The key is shown
once when it is created; FlowForge only keeps a hash of it.
Send the key in either header:
Authorization: Bearer ffk_…
X-API-Key: ffk_…
| Method | Path | Action | Ability |
|---|---|---|---|
GET |
/api/v1/workflows |
List workflows | workflows:read |
GET |
/api/v1/workflows/{id} |
Get a workflow | workflows:read |
POST |
/api/v1/workflows |
Create a workflow | workflows:write |
PUT |
/api/v1/workflows/{id} |
Update a workflow | workflows:write |
DELETE |
/api/v1/workflows/{id} |
Delete a workflow | workflows:write |
POST |
/api/v1/workflows/{id}/activate |
Activate a workflow | workflows:write |
POST |
/api/v1/workflows/{id}/deactivate |
Deactivate a workflow | workflows:write |
POST |
/api/v1/workflows/{id}/run |
Run a workflow | workflows:execute |
POST |
/api/v1/executions/{id}/cancel |
Cancel a running execution | workflows:execute |
GET |
/api/v1/executions |
List executions (filter with status and workflow_id) |
executions:read |
GET |
/api/v1/executions/{id} |
Get an execution and its steps | executions:read |
GET |
/api/v1/credentials |
List credentials (names and types only) | credentials:read |
POST |
/api/v1/credentials |
Create a credential | credentials:write |
DELETE |
/api/v1/credentials/{id} |
Delete a credential | credentials:write |
GET |
/api/v1/node-types |
List available nodes and their settings | any key |
Run a workflow and wait for its result:
curl -X POST https://<your-flowforge>/api/v1/workflows/$ID/run \
-H "Authorization: Bearer $KEY" \
-H 'Content-Type: application/json' \
-d '{"input": {"customer": "Ada"}, "wait": true}'
Without "wait": true, the call returns at once with the execution ID. Poll
GET /api/v1/executions/{id} for the result.
MCP server
Each workspace has a Model Context Protocol (MCP) server, so AI agents such as Claude can call your workflows as tools:
POST https://<your-flowforge>/api/mcp/<workspace-slug>
- Every active workflow that starts with an MCP tool trigger is listed as a tool, with the trigger's name, description and input schema.
- When an agent calls the tool, the workflow runs with the arguments as
$json. The final output, or the body set by a Respond to webhook node, is returned as the tool result. - A call waits up to 60 seconds by default for the workflow to finish.
- Clients authenticate with a workspace API key that has the
workflows:executeability.
For example, to add a workspace to Claude Code:
claude mcp add --transport http acme https://<your-flowforge>/api/mcp/acme \
--header "Authorization: Bearer ffk_…"
Browser-based MCP clients must send an Origin that matches your FlowForge URL or is listed
in AUTOMATION_MCP_ALLOWED_ORIGINS. Server-side clients are not affected.
Administration
Workspace limits
Platform administrators can set these per workspace under Platform → All tenants:
- Maximum workflows
- Maximum executions per month
- Webhook requests per minute
- Suspended: a suspended workspace cannot run workflows.
Server settings
Web application settings live in /var/www/automation/web/.env. Settings are cached, so after
changing them run this from /var/www/automation/web (on Ubuntu, use
www-data instead of apache):
sudo -u apache php artisan optimize
| Setting | Default | Meaning |
|---|---|---|
AUTOMATION_WEBHOOK_WAIT_TIMEOUT |
25 | Seconds a "wait for result" webhook stays open before replying 202 with the execution ID. |
AUTOMATION_WEBHOOK_MAX_BODY_KB |
1024 | Largest webhook request body accepted, in KB. |
AUTOMATION_MCP_TOOL_TIMEOUT |
60 | Seconds an MCP tool call waits for its workflow to finish. |
AUTOMATION_MCP_ALLOWED_ORIGINS |
(empty) | Comma-separated origins allowed for browser-based MCP clients, in addition to APP_URL. |
AUTOMATION_EXECUTION_RETENTION_DAYS |
30 | Executions older than this are deleted by the nightly scheduler. |
TRUSTED_PROXIES |
(empty) | Set to * when running behind an Application Load Balancer so HTTPS and client IPs are detected correctly. |
Engine settings live in /var/www/automation/engine/.env. Restart the engine after changing
them.
| Setting | Default | Meaning |
|---|---|---|
ENGINE_CONCURRENCY |
10 | Executions running in parallel per engine process. |
ENGINE_TENANT_CONCURRENCY |
3 | Maximum parallel executions per workspace per engine process. |
ENGINE_EXECUTION_TIMEOUT_SECONDS |
300 | Hard ceiling on a run. A workflow's own timeout can only lower it. |
ENGINE_MAX_STEPS |
500 | Stops runaway loops. |
ENGINE_SANDBOX_MEMORY_MB |
64 | Memory limit for Code nodes, per execution. |
ENGINE_ALLOW_PRIVATE_NETWORKS |
false | When false, workflows cannot reach localhost, private (RFC 1918) or link-local addresses, including the EC2 metadata service. |
ENGINE_HEALTH_PORT |
8790 | Port for the local health check. |
Health, logs and restarts
# Engine status, restart and logs
sudo systemctl status flowforge-engine
sudo systemctl restart flowforge-engine
sudo journalctl -u flowforge-engine -f
# Engine health check (on the instance)
curl http://127.0.0.1:8790/health
# Web application log
sudo tail -f /var/www/automation/web/storage/logs/laravel.log
You can run more than one engine process, on the same host or on other hosts that can reach the
database, to handle more work. Engines coordinate through the database and need the same
APP_KEY as the web application.
Backups and retention
All your data is in the MySQL database, apart from the configuration in the two .env
files. Back up both. Keep APP_KEY safe: without it, stored credentials cannot be
decrypted. With RDS, use automated backups and snapshots. With the local database, take regular
snapshots of the instance's EBS volume.
Run history older than AUTOMATION_EXECUTION_RETENTION_DAYS (30 days by default) is
deleted automatically each night.
HTTPS certificates
Without a load balancer, the instance starts with a self-signed certificate. For production, either
redeploy with a CertificateArn so an Application Load Balancer serves your ACM
certificate, or install a certificate for your domain on the instance's Apache server.
Upgrades
New FlowForge versions are published as new versions of the AWS Marketplace listing. Before upgrading an existing installation, take a database backup, then contact support and we will walk you through moving your instance to the new version.
Security
- Workspace isolation. Every record belongs to a workspace, and every query is limited to the current workspace. Requests that name another workspace's records return "not found". This is covered by automated tests on every page and API route.
- Encrypted credentials. Secrets are encrypted at rest and decrypted only inside the engine at run time. They are never returned by the interface or the API.
- Hashed API keys. Only a SHA-256 hash of each key is stored. Keys can be limited by ability and given an expiry date.
- Sandboxed code. Code nodes run in an isolated V8 sandbox with no network, filesystem or Node APIs, and with memory and time limits.
- Network guard. By default, workflows cannot reach localhost, private networks or the EC2 metadata service. The check runs before every request and every redirect.
- Locked setup. The setup wizard requires proof of instance ownership and is disabled permanently once setup completes.
- Hardened image. The AMI requires IMDSv2, and is shipped without SSH keys, passwords or secrets. Each instance generates its own on first boot.
Known limitations
- Each node passes a single JSON value to the next. Use Split into items to fan out. There is no merge or join node yet.
- A Wait node holds a worker slot while it waits, for up to 300 seconds. Longer delays are not supported yet.
- OAuth2 supports the client credentials flow only. The authorization code flow (signing in as a user) is not available yet.
- There are no dedicated nodes for services such as Google Workspace. Use HTTP Request with a credential instead.
- The network guard checks DNS before each request, but a hostile DNS server could still change its answer between the check and the connection. For strict environments, also route the engine through an egress proxy.
Getting help
Questions, deployment help or a problem in production: use the form below, email support@str8in.com or call 803 919 3252. If you subscribed through AWS Marketplace, include your AWS account ID so we can find your subscription.