These docs cover the S6S workflow engine — the developer playground layer underneath the product. For the main AI Visibility product (brand monitoring, the unified Report, fix actions), start at /docs/ai-visibility.
Recipe Format
The agent-friendly JSON workflow definition. Recipes compile to the internal IR for execution.
What is a Recipe?
A Recipe is a JSON object that describes a complete workflow. It is the format that agents (and humans) use to define what S6S should execute. Recipes are designed to be simple to produce — no UUIDs, no canvas positions, no internal plumbing.
When you deploy a Recipe, S6S validates it, compiles it to the full internal representation (IR), binds credentials, and begins execution. The compilation pipeline:
Recipe (agent-friendly JSON)
→ compileRecipe() → IR Workflow (typed, UUID-based, positioned)
→ compile() → CompiledPlan (topological order, parallel groups, retry policies)Top-level Fields
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Human-readable workflow name |
| trigger | object | Yes | How the workflow starts: manual, schedule, or webhook |
| steps | array | Yes | Array of step definitions to execute |
| flow | array | Yes | Execution order by step ref names. Strings for sequential, nested arrays for parallel. |
| on_error | object | No | Per-step error handling: { step_ref: { action: "stop" | "continue" | "retry" } } |
| slug | string | No | URL-safe identifier. Auto-generated from name if omitted. |
| autoRun | boolean | No | Set false to deploy without immediate execution (default: true). Webhook-triggered workflows never auto-run. |
| callback_url | string | No | URL to POST when the run completes. Callback body: { run_id, status, timestamp }. |
| waitForCompletion | boolean | No | If true, the deploy request blocks until the run finishes and returns inline results. |
{
"name": "Hello World",
"trigger": { "type": "manual" },
"steps": [
{
"ref": "greet",
"action": "transform",
"config": { "template": { "message": "Hello from S6S!" } }
}
],
"flow": ["greet"]
}Trigger Types
The trigger field determines how the workflow starts. Three types are supported:
Manual
Runs immediately on deploy (if autoRun is true) or when triggered via the API or dashboard.
"trigger": { "type": "manual" }Schedule
Runs on a cron schedule. The worker checks eligible schedules every 60 seconds.
"trigger": {
"type": "schedule",
"cron": "0 */4 * * *",
"timezone": "America/New_York"
}Schedule triggers provide {{trigger.output.fired_at}} — an ISO timestamp of when the schedule fired. This field is only available on schedule triggers, not webhooks.
Webhook
Runs when an HTTP request hits the webhook URL. After deployment, S6S returns an invoke URL and a secret token for authentication.
"trigger": {
"type": "webhook",
"path": "/new-lead",
"method": "POST"
}Webhook trigger outputs are available via expressions:
- {{trigger.output.body}} — request body payload
- {{trigger.output.headers}} — request headers
- {{trigger.output.query}} — query string parameters
- {{trigger.output.method}} — HTTP method
Steps
Each step defines a single action in the workflow. Steps are referenced by their ref in the flow array and in expressions.
| Field | Type | Required | Description |
|---|---|---|---|
| ref | string | Yes | Unique step reference. Used in flow, expressions, and on_error. Must be unique within the recipe. |
| action | string | Yes | Built-in action type or provider.action syntax for dynamic integrations. |
| config | object | Yes | Action-specific configuration. Values support double-brace expressions. |
| credential | string | No | Provider name for credential injection (e.g. "slack", "google", "telegram"). |
| run_if | string | No | Expression evaluated at runtime. If falsy, the step is skipped (not failed). |
| on_error | object | No | Inline error policy: { action: "stop" | "continue" | "retry" }. Can also be set at recipe top level. |
{
"ref": "notify_slack",
"action": "slack",
"config": {
"channel": "#sales",
"textTemplate": "New lead: {{format.output.text}}"
},
"credential": "slack",
"run_if": "{{format.output.should_notify}} == true"
}Actions
The action field on each step determines what it does. S6S supports built-in actions and dynamic integrations.
Built-in Actions
| Action | Credential | Description |
|---|---|---|
| http | No | Make HTTP requests to any URL. Supports GET, POST, PUT, PATCH, DELETE. |
| transform | No | Reshape data between steps using a JSON template with expressions. |
| code | No | Execute JavaScript. Access previous outputs via steps and trigger objects. |
| ai | No | Call an LLM (GPT, Claude) within a workflow step. Supports text and JSON output. |
| condition | No | Evaluate a boolean condition to branch the flow. |
| switch | No | Multi-way routing based on expression value. |
| filter | No | Filter, map, find, or reduce an array. |
| loop | No | Iterate over an array (max 1000 items). |
| wait | No | Pause execution for a specified number of seconds. |
| SMTP | Send email via SMTP. | |
| slack | Slack | Post messages to Slack channels. |
| discord | Discord | Send messages to Discord channels. |
| telegram | Telegram | Send messages, photos, or documents via Telegram bot. |
| gmail | Search, read, and send emails via Gmail API. | |
| imap | IMAP | Fetch and search emails via IMAP (Outlook, Yahoo, etc.). |
| rss | No | Fetch and parse RSS/Atom feeds. |
| Post tweets or search Twitter. | ||
| google_sheets | Read, write, or append rows in Google Sheets. | |
| google_drive | Upload, download, or list files in Google Drive. | |
| database | Database | Execute SQL queries against PostgreSQL. |
| webhook_response | No | Set the HTTP response for webhook-triggered workflows. |
| execute_workflow | No | Trigger another S6S workflow by slug (sub-workflow execution). |
| stop_and_error | No | Immediately halt the workflow with a custom error message. |
Dynamic Integrations
For any platform not covered by built-in actions, use the provider.action syntax. S6S resolves the provider, injects credentials, and makes the correct API call.
| Example | Description |
|---|---|
| notion.create_page | Create a page in Notion |
| pinterest.create_pin | Create a pin on Pinterest |
| newsapi.get_top_headlines | Fetch top headlines from NewsAPI |
| stripe.create_charge | Create a charge via Stripe API |
If a provider is unknown, S6S automatically fetches its API documentation and learns the integration via LLM. The result is cached and reused for future workflows. Prefer provider.action over raw http for any API that requires authentication.
Flow Declaration
The flow array controls execution order. It contains step refs as strings, with nested arrays for parallel execution.
Sequential
Steps listed as strings execute one after another:
"flow": ["fetch_data", "process", "notify"] // fetch_data → process → notify
Parallel
Steps inside a nested array run concurrently. The next sequential step waits for all parallel steps to complete:
"flow": ["prepare", ["post_slack", "post_discord", "send_email"], "summarize"] // prepare → (post_slack + post_discord + send_email in parallel) → summarize
Mixed
Combine sequential and parallel sections freely:
"flow": [ "step_a", // sequential "step_b", // sequential ["step_c", "step_d"], // parallel "step_e" // sequential (after both c and d complete) ]
Condition branching
Use a flow tuple with a condition step to branch execution:
"flow": ["prep", ["check", "yes_path", "no_path"], "finalize"] // prep → evaluate check → run yes_path or no_path → finalize
The first element must be a step with "action": "condition". The second is the true branch, the third (optional) is the false branch.
Important: Every step in steps that you want executed must appear in the flow array. Steps defined but not in flow will never run.
Error Handling
The on_error field defines per-step error policies. It can be set at the recipe top level (keyed by step ref) or inline on each step.
| Action | Behavior |
|---|---|
| stop | Halt the entire workflow immediately (default). |
| continue | Skip the failed step and proceed to the next step in the flow. |
| retry | Retry the step with configurable max_attempts and backoff. |
"on_error": {
"flaky_api": {
"action": "retry",
"retry": { "max_attempts": 3 }
},
"optional_notify": { "action": "continue" },
"critical_step": { "action": "stop" }
}When retry is used, the step is retried up to max_attempts times. You can also specify backoffMs to control the delay between retries.
Conditional Execution
Steps can include an optional run_if field — an expression evaluated at runtime. If the expression resolves to a falsy value, the step is skipped, not failed. The overall run still succeeds.
Supports simple comparisons: >, <, >=, <=, ==, !=
{
"ref": "notify",
"action": "telegram",
"config": {
"action": "send_message",
"text": "High value alert: {{get_total.output.amount}}"
},
"credential": "telegram",
"run_if": "{{get_total.output.amount}} > 10000"
}If the amount is 5000, the notify step is skipped and the run finishes as succeeded.
Full Example
A realistic multi-step workflow with a webhook trigger, parallel branches, error handling, and credential references:
{
"name": "New Lead Notification Pipeline",
"trigger": {
"type": "webhook",
"path": "/new-lead",
"method": "POST"
},
"steps": [
{
"ref": "format_message",
"action": "transform",
"config": {
"template": {
"text": "New lead: {{trigger.output.body.name}} ({{trigger.output.body.email}})",
"name": "{{trigger.output.body.name}}",
"email": "{{trigger.output.body.email}}"
}
}
},
{
"ref": "notify_slack",
"action": "slack",
"config": {
"channel": "#sales",
"textTemplate": "{{format_message.output.text}}"
},
"credential": "slack"
},
{
"ref": "log_to_notion",
"action": "notion.create_page",
"config": {
"parent_id": "your-database-id",
"properties": {
"Name": "{{format_message.output.name}}",
"Email": "{{format_message.output.email}}"
}
},
"credential": "notion"
},
{
"ref": "respond",
"action": "webhook_response",
"config": {
"statusCode": 200,
"body": { "status": "accepted", "run_id": "{{run.id}}" }
}
}
],
"flow": [
"format_message",
["notify_slack", "log_to_notion"],
"respond"
],
"on_error": {
"notify_slack": { "action": "continue" },
"log_to_notion": { "action": "retry", "retry": { "max_attempts": 3 } }
},
"callback_url": "https://your-server.com/on-complete"
}This workflow receives a lead via webhook, formats the data, then notifies Slack and logs to Notion in parallel. If Slack fails, execution continues. If Notion fails, it retries up to 3 times. Finally, a webhook response is sent back to the caller.
Next: Expressions
Learn how to reference data between steps with template expressions.