Flows
Automations without servers: when a webhook, schedule, table or collection change, queue message or form fires, run steps — HTTP calls, functions, database writes, email, AI — with branches, loops, waits, retries and a full run history.
A flow is a graph of steps. One or more triggers start it; each step gets the items the step before it sent, does its work, and passes items on. Flows run alongside your functions: every run is durable (a crash or restart resumes it), every step is recorded with its input, output and error, and a failed run can be retried from the step that failed.
#Triggers and steps
| Kind | Steps |
|---|---|
| Triggers | Manual (Test run), Webhook, Schedule, Sovaria event (table rows, documents, auth users, storage objects, realtime messages), Queue message, Form, Sub-flow, Error flow; app triggers poll Google Drive, Sheets, Gmail, Calendar and Telegram. |
| Flow control | If, Switch, Filter, Merge, Loop over items, Wait (time or a signal), Approval, Sub-flow, Respond to webhook, Stop with error. |
| Data | Edit fields (set / rename / keep), Code (JavaScript or Python in the sandbox), Sort, Limit, Remove duplicates, Aggregate, Summarize, Split out, Date & time, Crypto, HTML extract, Convert, Compare. |
| Sovaria | SQL, Table rows (get / insert / update / upsert / delete), Documents, Storage, Send message (email, SMS, push, in-app), Realtime publish, Run a function, Send to a queue, Analytics event. |
| AI | AI (complete, classify, extract, summarise), Agent run, Knowledge base search — billed as Sovariq credits. |
| Network and apps | HTTP Request (any public API, with pagination), and 13 apps (Google Drive, Sheets, Gmail and Calendar; Microsoft Outlook, OneDrive and Excel; Slack, Notion, GitHub, HubSpot, Airtable, Stripe, Twilio, Discord, Telegram, OpenAI, Anthropic) plus generic OAuth2, API-key, bearer, Basic and header auth, through Integrations connections. Apps that sign in with OAuth use Sovaria's app where staff have set it up, or your project's own OAuth app (Connections › Use your own OAuth app, which shows the redirect URL to register); the Connections page says which is available. |
Any text setting can hold an expression: {{ $json.email.toLowerCase() }} reads the current item, {{ $('Shape').item.json.total }} reads another step, $now, $execution.id and $vars are there too. Expressions are evaluated safely (no code runs); use a Code step for logic.
#Build one in the console
Functions › Flows › New flow
Start blank or from a template (Stripe payment → row + email, form → Slack, nightly report…).
Pick a trigger, then add steps
Click + on a step’s output to add the next one, or drag from an output handle onto a step. Hover or click a connection for + (add a step in between; later steps move right) and the bin (remove it); Delete removes a selected connection. From the keyboard: select a step, press ] or [ to move through its connections, or use the Connections list in the step’s settings. Click a step to set it up; settings with
{{ }}take expressions.Test run
Run the draft once with sample data (JSON), or listen for one real request on the webhook’s test URL. Each step shows the items it got and sent; click a step to see its input and output.
Publish, then turn it on
Edits save as a draft. Publish makes a version; the live flow runs only the published version, and only while it’s on. Versions lets you put an older version back in the draft.
#Webhooks are always protected
A webhook URL looks like https://<project-ref>.sovaria.in/hooks/flows/<flow-id>/<path>?token=<flow token>. Every webhook checks the caller one of four ways:
| Authentication | The caller sends |
|---|---|
| URL token (default) | The full URL from the editor (it ends in ?token=…), or the token in an X-Flow-Token header. Sovaria makes one token per flow; New token in the step panel (or sov flows rotate-token) replaces it and old URLs get 401 at once. |
| Header value | A header (default X-Webhook-Token) equal to a project secret. |
| Basic auth | Authorization: Basic … matching a project secret user:password. |
| HMAC signature | A signature with a project secret in the sender's own format. Signature format: Stripe (Stripe-Signature: t=…,v1=…), Slack (X-Slack-Signature with X-Slack-Request-Timestamp), GitHub (X-Hub-Signature-256: sha256=…), Shopify (X-Shopify-Hmac-Sha256, base64), Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature: Svix, Resend, Clerk), or a plain HMAC-SHA256 of the body as hex or base64. Detect from the header (the default) picks the format from the header name. |
- Stripe, Slack and Standard Webhooks sign a timestamp too: a request more than 5 minutes old (change it with Timestamp tolerance, 30 seconds to 1 hour) is refused. A second delivery of a request already accepted (a sender's retry, GitHub's Redeliver) is answered
200 {"ok": true, "duplicate": true}without running the flow again; GitHub and Shopify sign no timestamp, so their repeats are matched by delivery id and body: that catches duplicate deliveries, but a captured request replayed with a new delivery id isn't caught. Keep their URLs and secrets private. - Signatures are compared in constant time. Put the provider's signing secret in a project secret (for Stripe the endpoint's
whsec_…, for Slack the app's Signing Secret) and name it in Secret. - Slack's Events API checks a new URL with a signed
url_verificationrequest: the webhook answers its challenge itself and the flow doesn't run. The Slack event → database row template is set up for this.
#Runs, errors and retries
- Run history (Runs in the editor,
sov flows runs): every execution with its mode (test run, webhook, schedule, event…), version, time and status; open one to see each step’s items, time and error. - When a step fails it stops the run by default. Per step you can instead *continue* with the error as the item, send the error to an extra error output, and retry up to 10 times with a wait between tries.
- Retry a run from the step that failed — it gets the same input the failed step had — or from the start on the latest published version. Cancel stops a run that is still going.
- Error flow: in the flow’s settings, name another flow to run whenever this one fails (it starts with an Error trigger).
- Secrets never show in runs. Any project secret or connection token a step used is replaced by
[secret]in that step’s output, errors and webhook responses, even when an API echoes it back.
#Security
- HTTP Request steps reach public addresses only: URLs are checked (no IP literals, no localhost or internal names) and calls go through the same egress proxy as functions, which refuses private, loopback and link-local addresses on every redirect.
- Steps that touch your data (SQL, rows, documents, storage, messages) call the Sovaria API as the project’s service role: your access rules for the service role, plan limits, metering and the audit log all apply.
- Flows belong to one project and environment; nothing crosses projects. Editing and publishing need functions.deploy; test runs, retries and approvals need functions.invoke; viewing needs functions.read.
- Every run is capped: steps per run (loops count every round), items per step, data per run and the longest wait — see the limits below. A runaway loop stops with
PLAN_LIMIT_REACHED.
#Plan limits
| Free | Pro | Ultra | Team | Enterprise | |
|---|---|---|---|---|---|
| Flows per project (flow.flows) | 5 | 50 | 200 | 500 | 2,000 |
| Flow runs per month (flow.executions_month) | 1,000 | 25,000 | 100,000 | 500,000 | 2,000,000 |
| Steps per run, every loop round counted (flow.nodes_per_run) | 200 | 2,000 | 5,000 | 10,000 | 20,000 |
| Shortest gap between scheduled runs (flow.schedule_min_minutes) | 15 min | 5 min | 1 min | 1 min | 1 min |
| Highest form submissions a minute per visitor a flow may allow (flow.form_per_visitor_per_min) | 30 | 120 | 300 | 600 | 1,000 |
| Unfinished runs per project (flow.executions_active) | 20 | 1,000 | 5,000 | 20,000 | 100,000 |
| Run history kept (flow.history_days) | 1 day | 7 days | 14 days | 30 days | 30 days |
| Longest Wait or Approval (flow.wait_days) | 1 day | 7 days | 30 days | 90 days | 365 days |
| Items one step may output (flow.items_per_node) | 2,000 | 20,000 | 50,000 | 100,000 | 100,000 |
| Data one run may hold (flow.execution_bytes) | 4 MB | 16 MB | 32 MB | 64 MB | 64 MB |
Runs per month count executions started by triggers (webhooks, forms, schedules, events, queues, app polling). Test runs, retries and sub-flow calls don’t count. A redelivered event starts nothing new and isn’t counted twice. When the month’s runs are used up, triggers are refused with PLAN_LIMIT_REACHED and the flow’s run history shows each refused run. Schedules faster than your plan allows are refused when you turn the flow on; app triggers poll no faster than it. A public form takes at most 20 submissions a minute from one visitor (an IPv6 /64 counts as one) and the flow’s webhook rate limit overall. A kiosk or an event check-in where everyone shares one network can raise the per-visitor cap in the flow’s settings (Form submissions per visitor a minute, form_per_visitor_per_min, up to your plan’s flow.form_per_visitor_per_min; after a downgrade the plan’s value applies); a webhook that answers after the flow ran waits up to 25 seconds, or answers 202 with the execution id at once when Sovaria is already waiting on many such calls.
#From code and the CLI
sov flows create "New signup" --template stripe-paid
sov flows pull flw_123 > flow.json # edit the graph, then
sov flows push flw_123 flow.json
sov flows test flw_123 --data '{"email": "ada@example.com"}'
sov flows publish flw_123 --note "first version" && sov flows on flw_123
sov flows runs flw_123 --status error
sov flows retry fex_456 # from the failed step
sov flows rotate-token flw_123 # new webhook URL token#Coming from Firebase or Supabase?
| Platform | What this is called there |
|---|---|
| Firebase | Cloud Workflows / Eventarc (and Zapier-style tools) |
| Supabase | No built-in equivalent (n8n or Zapier alongside Supabase) |
| Sovaria | Flows |
Moving an existing app? Migrate from Supabase or Firebase imports users (with their passwords), data, files and rules.