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

FieldTypeRequiredDescription
namestringYesHuman-readable workflow name
triggerobjectYesHow the workflow starts: manual, schedule, or webhook
stepsarrayYesArray of step definitions to execute
flowarrayYesExecution order by step ref names. Strings for sequential, nested arrays for parallel.
on_errorobjectNoPer-step error handling: { step_ref: { action: "stop" | "continue" | "retry" } }
slugstringNoURL-safe identifier. Auto-generated from name if omitted.
autoRunbooleanNoSet false to deploy without immediate execution (default: true). Webhook-triggered workflows never auto-run.
callback_urlstringNoURL to POST when the run completes. Callback body: { run_id, status, timestamp }.
waitForCompletionbooleanNoIf true, the deploy request blocks until the run finishes and returns inline results.
Minimal Recipe
{
  "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.

FieldTypeRequiredDescription
refstringYesUnique step reference. Used in flow, expressions, and on_error. Must be unique within the recipe.
actionstringYesBuilt-in action type or provider.action syntax for dynamic integrations.
configobjectYesAction-specific configuration. Values support double-brace expressions.
credentialstringNoProvider name for credential injection (e.g. "slack", "google", "telegram").
run_ifstringNoExpression evaluated at runtime. If falsy, the step is skipped (not failed).
on_errorobjectNoInline error policy: { action: "stop" | "continue" | "retry" }. Can also be set at recipe top level.
Step example
{
  "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

ActionCredentialDescription
httpNoMake HTTP requests to any URL. Supports GET, POST, PUT, PATCH, DELETE.
transformNoReshape data between steps using a JSON template with expressions.
codeNoExecute JavaScript. Access previous outputs via steps and trigger objects.
aiNoCall an LLM (GPT, Claude) within a workflow step. Supports text and JSON output.
conditionNoEvaluate a boolean condition to branch the flow.
switchNoMulti-way routing based on expression value.
filterNoFilter, map, find, or reduce an array.
loopNoIterate over an array (max 1000 items).
waitNoPause execution for a specified number of seconds.
emailSMTPSend email via SMTP.
slackSlackPost messages to Slack channels.
discordDiscordSend messages to Discord channels.
telegramTelegramSend messages, photos, or documents via Telegram bot.
gmailGoogleSearch, read, and send emails via Gmail API.
imapIMAPFetch and search emails via IMAP (Outlook, Yahoo, etc.).
rssNoFetch and parse RSS/Atom feeds.
twitterTwitterPost tweets or search Twitter.
google_sheetsGoogleRead, write, or append rows in Google Sheets.
google_driveGoogleUpload, download, or list files in Google Drive.
databaseDatabaseExecute SQL queries against PostgreSQL.
webhook_responseNoSet the HTTP response for webhook-triggered workflows.
execute_workflowNoTrigger another S6S workflow by slug (sub-workflow execution).
stop_and_errorNoImmediately 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.

ExampleDescription
notion.create_pageCreate a page in Notion
pinterest.create_pinCreate a pin on Pinterest
newsapi.get_top_headlinesFetch top headlines from NewsAPI
stripe.create_chargeCreate 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.

ActionBehavior
stopHalt the entire workflow immediately (default).
continueSkip the failed step and proceed to the next step in the flow.
retryRetry the step with configurable max_attempts and backoff.
Top-level on_error
"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: >, <, >=, <=, ==, !=

Only notify if amount exceeds threshold
{
  "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:

New Lead Notification Pipeline
{
  "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.