Operations reference
Configuration reference for every operation available in automation flows, grouped by category.
#Operations reference
A flow is a sequence of steps. Each step has a unique key, an operation type, and a configuration object:
{
"key": "fetch_orders",
"type": "crud_read",
"config": {
"collection": "orders",
"filter": { "status": "pending" },
"limit": 100
}
}
Most configuration fields accept template expressions such as {{$trigger.body.id}} or {{= $sum($steps.fetch_orders.records.total) }} — see expressions. Each step's output is available to later steps as $steps.<step_key>.
The shape under $steps.<step_key> is the operation's own output shape, and it is not always
the value you might expect. In particular
run_scriptwraps the script's return value, so the path is{{$steps.<step_key>.output.<field>}}— not{{$steps.<step_key>.<field>}}. Each operation's output shape is listed with it below.A template path that doesn't resolve produces an empty value rather than an error, and writes the reason to the step log — check there first if a field arrives unexpectedly blank.
#Step-level settings
Every step, regardless of operation type, supports these optional settings:
| Setting | Values | Description |
|---|---|---|
condition |
rule tree | Skip the step when the condition evaluates to false |
onError |
stop, continue, retry, fallback |
What to do when the step fails |
retryConfig |
{ maxAttempts, delayMs, backoff } |
Retry settings (backoff: fixed or exponential) — used with onError: retry |
fallbackValue |
any | Value used as the step output when it fails — used with onError: fallback |
#Flow options
Options apply to the whole flow:
| Option | Default | Description |
|---|---|---|
timeout_ms |
30000 |
Maximum execution time for the entire flow |
max_retries |
0 |
Automatic retries if the flow fails |
concurrency |
sequential |
sequential (one instance at a time), parallel, or a number of concurrent instances |
error_handling |
stop_on_first |
stop_on_first or continue_all |
log_level |
full |
full, errors_only, or none |
#Data operations
#crud_action — Collection Records
Create, read, update, delete, or query records in a collection. One operation, five actions — the fields you configure depend on the action.
| Field | Type | Required | Description |
|---|---|---|---|
collection |
string | Yes | Target collection |
action |
enum | Yes | create, read, update, delete, or query (default: read) |
data |
object | create/update | Field values for the record |
id |
string | No | Single record ID (update/delete) |
ids |
array | No | Multiple record IDs (update/delete) |
filter |
object | No | Filter criteria, e.g. {"status": "active"} (read/update/delete/query) |
sort |
array | No | Array of {field, order} objects (read/query) |
limit |
number | No | Maximum records to return, 1–10000 (default: 25) |
offset |
number | No | Records to skip (read) |
fields |
array | No | Specific fields to return; empty means all (read) |
select |
array | No | Fields to include in query results |
groupBy |
array | No | Fields to group query results by |
aggregate |
array | No | Array of {field, fn} where fn is count, sum, avg, min, or max |
permanent |
boolean | No | Permanently delete instead of soft-delete (default: false) |
The five operations below are focused variants of crud_action. They behave identically — pick whichever reads better in your flow.
#crud_create — Create Record
| Field | Type | Required | Description |
|---|---|---|---|
collection |
string | Yes | Target collection |
data |
object or array | Yes | Field values for the new record(s) |
#crud_read — Read Records
| Field | Type | Required | Description |
|---|---|---|---|
collection |
string | Yes | Target collection |
filter |
object | No | Filter criteria |
sort |
array | No | Array of {field, order} objects |
limit |
number | No | 1–1000 (default: 25) |
offset |
number | No | Records to skip (default: 0) |
fields |
array | No | Specific fields to return |
#crud_update — Update Records
| Field | Type | Required | Description |
|---|---|---|---|
collection |
string | Yes | Target collection |
data |
object | Yes | Fields and values to update |
id |
string | No | Single record ID |
ids |
array | No | Multiple record IDs |
filter |
object | No | Filter matching the records to update |
#crud_delete — Delete Records
| Field | Type | Required | Description |
|---|---|---|---|
collection |
string | Yes | Target collection |
id |
string | No | Single record ID |
ids |
array | No | Multiple record IDs |
filter |
object | No | Filter matching the records to delete |
permanent |
boolean | No | Permanently delete instead of soft-delete (default: false) |
#crud_query — Query Records
Aggregation and grouping over a collection.
| Field | Type | Required | Description |
|---|---|---|---|
collection |
string | Yes | Target collection |
select |
array | No | Fields to include in results |
filter |
object | No | Filter criteria |
groupBy |
array | No | Fields to group by |
aggregate |
array | No | Array of {field, fn} — count, sum, avg, min, max |
sort |
array | No | Sort order |
limit |
number | No | 1–10000 (default: 100) |
#build_search_index — Build Search Index
Compiles a search index from its source collections into an R2 SQLite file.
| Field | Type | Required | Description |
|---|---|---|---|
indexName |
string | Yes | The name of the search index to build |
#Logic operations
#condition — Condition
If/else branching based on a rule tree. Rules compare a field against a value with operators such as eq, neq, gt, gte, lt, lte, in, nin, contains, starts_with, ends_with, is_null, is_empty, and matches, combined with _and/_or groups.
| Field | Type | Required | Description |
|---|---|---|---|
rules |
object | Yes | Condition rule tree — use the visual builder or edit the JSON directly |
#switch — Switch
Multi-branch routing based on matching a value against cases.
| Field | Type | Required | Description |
|---|---|---|---|
value |
template | Yes | Value to match, e.g. {{$trigger.body.status}} |
cases |
array | Yes | Array of {match, steps} objects |
default |
array | No | Steps to run if no case matches |
#loop — Loop
Iterates over an array, executing nested operations for each item. Inside the loop, reference the current item as {{$steps.<loop_key>.item}} and the zero-based index as {{$steps.<loop_key>.index}}.
| Field | Type | Required | Description |
|---|---|---|---|
items |
template | Yes | Array to iterate over, e.g. {{$steps.fetch_orders.records}} |
mode |
enum | No | serial (default), parallel, or batch |
batchSize |
number | No | Items per batch in batch mode, 1–50 (default: 10) |
operations |
array | Yes | Operations to execute for each item |
#stop — Stop Flow
Halts the flow with a success or error status. In an event_filter flow, stopping with an error status rejects the triggering write.
| Field | Type | Required | Description |
|---|---|---|---|
status |
enum | No | success (default) or error |
message |
template | No | Exit message (default: "Flow execution stopped.") |
#throw_error — Throw Error
Aborts the flow with an error message and HTTP status code.
| Field | Type | Required | Description |
|---|---|---|---|
message |
template | Yes | Error message |
statusCode |
number | No | HTTP status code, 100–599 (default: 400) |
#set_variable — Set Variable
Stores a value for later steps to read via {{$steps.<step_key>}}.
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | Yes | Variable name |
value |
template | Yes | Value to store |
#trigger_flow — Trigger Flow
Executes another flow. Sub-flows nest up to 8 levels deep; circular calls are rejected.
| Field | Type | Required | Description |
|---|---|---|---|
flowId |
string | Yes | The flow to trigger |
data |
object | No | Payload passed to the triggered flow as $trigger.body |
async |
boolean | No | Fire and forget — don't wait for the sub-flow (default: false) |
#Transform and utility operations
#transform — Transform Data
Reshapes data through a pipeline of operations.
| Field | Type | Required | Description |
|---|---|---|---|
input |
template | Yes | Input data, e.g. {{$steps.fetch_orders.records}} |
operations |
array | Yes | Pipeline of operations: map, filter, pick, omit, merge, flatten, unique, sort, reverse, slice, groupBy |
For anything beyond a short pipeline, the run_expression operation with JSONata is usually clearer.
#json_parse — JSON Parse
Parses a JSON string and optionally extracts a value by path.
| Field | Type | Required | Description |
|---|---|---|---|
input |
template | Yes | String or object to parse |
path |
string | No | Dot-path to extract from the parsed result, e.g. data.items |
#text_format — Text Format
String manipulation via a template, a list of text operations, or both.
| Field | Type | Required | Description |
|---|---|---|---|
template |
text | No | Text template with variable substitution |
operations |
array | No | Text operations: concat, split, replace, regex_replace, trim, uppercase, lowercase, substring, pad |
#math — Math
Mathematical operations over an array of values or a formula.
| Field | Type | Required | Description |
|---|---|---|---|
operation |
enum | Yes | sum, avg, min, max, round, floor, ceil, abs, or formula |
values |
array | No | Numbers to operate on |
formula |
template | No | Formula expression, e.g. (a + b) * c (formula operation) |
precision |
number | No | Decimal precision, 0–20 |
#datetime — Date / Time
Date and time calculations.
| Field | Type | Required | Description |
|---|---|---|---|
operation |
enum | Yes | now, format, parse, add, subtract, diff, startOf, endOf |
value |
template | No | Input date/time string |
amount |
number | No | Units to add or subtract |
unit |
enum | No | milliseconds, seconds, minutes, hours, days, weeks, months, years |
format |
string | No | Format pattern, e.g. YYYY-MM-DD HH:mm:ss |
timezone |
string | No | Timezone name, e.g. Australia/Sydney |
#log — Log
Writes a message to the flow run log — useful for debugging.
| Field | Type | Required | Description |
|---|---|---|---|
message |
template | Yes | Message to log |
level |
enum | No | info (default), warn, error, debug |
#delay — Delay
Pauses the flow.
| Field | Type | Required | Description |
|---|---|---|---|
ms |
number | Yes | Milliseconds to wait, 0–25000 (default: 1000) |
Delays count towards the flow timeout (30 seconds by default). Long waits belong in a scheduled flow instead.
#render_lens_template — Render Lens Template
Renders a Lens template to HTML, CSV, or Markdown, optionally saving the result to R2.
| Field | Type | Required | Description |
|---|---|---|---|
templateId |
string | Yes | ID of the Lens template |
format |
enum | No | html (default), csv, or md |
urlParams |
object | No | Query parameters passed to the renderer |
saveToR2 |
boolean | No | Save the output to R2 (default: false) |
r2Key |
string | No | Custom R2 key for the output file |
#AI operations
AI operations count against your project's daily AI token quota. When the quota is exhausted, AI steps fail until the next day.
Requirements: AI operations run on Workers AI via the gateway's AI binding, which the standard deploy templates include. If a step fails with "AI provider … does not support" or the binding is absent (check GET /health), your instance's worker config is missing the [ai] block. provider currently supports cloudflare only — other providers are reserved for future use.
ai_classify is production-ready and a common building block for content moderation: classify a post's text into labels like ok / spam / abuse in a flow triggered on record create, then act on the result (flag, soft-delete, or queue for review) in subsequent steps.
#ai_generate — AI Generate Text
Generates text, with optional system prompt and JSON mode.
| Field | Type | Required | Description |
|---|---|---|---|
userPrompt |
text | Yes | The prompt to send to the model |
systemPrompt |
text | No | System/instruction prompt |
model |
string | No | Model ID, e.g. @cf/meta/llama-3.1-8b-instruct |
maxTokens |
number | No | Maximum tokens to generate |
temperature |
number | No | 0–2 (default: 0.7) |
responseFormat |
enum | No | text (default) or json |
jsonSchema |
object | No | Expected JSON response structure (JSON mode) |
provider |
string | No | AI provider (default: cloudflare) |
#ai_classify — AI Classify Text
Classifies text into one of several labels. Returns the best label, a confidence score, and scores for every label.
| Field | Type | Required | Description |
|---|---|---|---|
text |
template | Yes | Text to classify |
labels |
array | Yes | At least 2 classification labels |
model |
string | No | Model ID |
provider |
string | No | AI provider |
#ai_extract — AI Extract Structured Data
Extracts structured data from text according to a schema.
| Field | Type | Required | Description |
|---|---|---|---|
text |
template | Yes | Source text |
schema |
object | Yes | Object describing what to extract |
instructions |
text | No | Extra extraction instructions |
model |
string | No | Model ID |
provider |
string | No | AI provider |
#ai_image — AI Generate Image
Generates an image from a text prompt.
| Field | Type | Required | Description |
|---|---|---|---|
prompt |
text | Yes | Image prompt |
model |
string | No | Model ID |
width |
number | No | Image width in pixels |
height |
number | No | Image height in pixels |
#ai_review — AI Review
Reviews text against criteria and returns a score, feedback, issues, and suggestions.
| Field | Type | Required | Description |
|---|---|---|---|
content |
text | Yes | Content to review |
criteria |
array | No | Criteria to evaluate against |
threshold |
number | No | Score threshold for auto-approval, 0–1 (default: 0.7) |
model |
string | No | Model ID |
#ai_summarize — AI Summarize
Summarises text with a configurable length and style.
| Field | Type | Required | Description |
|---|---|---|---|
text |
text | Yes | Text to summarise |
style |
enum | No | brief (default), detailed, or bullets |
maxLength |
number | No | Maximum character length of the summary |
model |
string | No | Model ID |
#ai_translate — AI Translate
Translates text to a target language.
| Field | Type | Required | Description |
|---|---|---|---|
text |
text | Yes | Text to translate |
targetLanguage |
string | Yes | Target language, e.g. Spanish |
sourceLanguage |
string | No | Source language (auto-detected if empty) |
model |
string | No | Model ID |
#File operations
#file_read — Read File
Reads content from your project's R2 object storage.
| Field | Type | Required | Description |
|---|---|---|---|
key |
template | Yes | File key, e.g. uploads/report.csv |
encoding |
enum | No | text (default), base64, or json |
#file_upload — Upload File
Uploads content to R2 object storage.
| Field | Type | Required | Description |
|---|---|---|---|
key |
template | Yes | File key, e.g. exports/report.csv |
content |
text | Yes | File content (text or base64-encoded) |
contentType |
string | No | MIME type (default: application/octet-stream) |
metadata |
object | No | Custom metadata key/value pairs |
#file_transform — Transform File
Applies image transformations to stored files, using the server-side resizer. The source is read from wherever the file actually lives — a container-scoped or tenant-dedicated bucket included.
| Field | Type | Required | Description |
|---|---|---|---|
sourceKey |
template | Yes | Source file key |
destKey |
template | No | Destination key (derived from the source if empty) |
operations |
array | Yes | Array of {type, ...options}: resize, format, quality |
What the resizer can do:
resize—width,height(either or both) andfit.contain(default) andscale-downfit within the box and preserve aspect ratio.coverandcropneed a crop the resizer cannot perform.format—png,jpegorwebp. With noformatoperation the source's own format is kept; a source in a format that can be read but not written (gif, bmp, tiff) must name one.avifcannot be written.quality— webp output only.
A transform the resizer cannot perform fails the step, and nothing is written to the destination key. It never falls back to copying the source, which is what it used to do: the output was a byte-identical copy at a new key and the run reported success.
#s3_action — S3 / R2 File
Put, get, delete, list, or check files in any S3-compatible bucket via a storage connection.
| Field | Type | Required | Description |
|---|---|---|---|
connectionId |
string | Yes | Storage connection (create them in Settings → Storage Connections) |
action |
enum | Yes | put, get, delete, list, or head |
key |
template | put/get/delete/head | Object key |
content |
text | put | Content to upload (text or base64) |
contentType |
string | No | MIME type for uploads |
encoding |
enum | No | For get: text, base64, or json |
prefix |
string | No | Key prefix for list |
maxKeys |
number | No | Maximum objects to list |
#Network operations
Outbound requests are blocked from reaching internal and private network addresses.
#http_request — HTTP Request
Makes an outbound HTTP request. The output contains status, statusText, headers, body, and durationMs.
| Field | Type | Required | Description |
|---|---|---|---|
url |
template | Yes | Target URL (http or https) |
method |
enum | No | GET (default), POST, PUT, PATCH, DELETE, HEAD, OPTIONS |
headers |
object | No | Request headers |
body |
any | No | Request body — objects are JSON-encoded automatically |
responseType |
enum | No | json (default), text, or binary (base64) |
timeout_ms |
number | No | 100–30000 (default: 10000) |
auth |
object | No | {"type":"bearer","token":"…"}, {"type":"basic","username":"…","password":"…"}, or {"type":"api_key","header":"X-API-Key","token":"…"} |
#graphql_request — GraphQL Request
Sends a GraphQL query or mutation and returns the data and errors.
| Field | Type | Required | Description |
|---|---|---|---|
url |
template | Yes | GraphQL endpoint URL |
query |
code | Yes | Query or mutation string |
variables |
object | No | Query variables |
headers |
object | No | Request headers |
operationName |
string | No | For multi-operation documents |
timeout_ms |
number | No | 100–30000 (default: 10000) |
#webhook_dispatch — Webhook Dispatch
POSTs a JSON payload to a webhook URL with optional HMAC-SHA256 signing. See sending webhooks.
| Field | Type | Required | Description |
|---|---|---|---|
url |
template | Yes | Destination webhook URL |
payload |
object | No | JSON payload (defaults to the trigger body) |
headers |
object | No | Extra request headers |
secret |
string | No | HMAC-SHA256 shared secret — adds X-EmuView-Signature and X-EmuView-Timestamp headers |
timeout_ms |
number | No | 100–30000 (default: 10000) |
#Script operations
#run_expression — Run Expression
Evaluates a JSONata expression. Always available — no extra setup required.
| Field | Type | Required | Description |
|---|---|---|---|
expression |
code | Yes | JSONata expression. Access data via $trigger, $steps, $user, $data |
input |
object | No | Additional data merged into $data |
timeout_ms |
number | No | 100–10000 (default: 5000) |
#run_script — Run Script (JavaScript)
Executes custom JavaScript in an isolated sandbox. See scripts for the sandbox environment, limits, and requirements.
| Field | Type | Required | Description |
|---|---|---|---|
code |
code | No* | Inline JavaScript |
scriptId |
string | No* | Saved script to execute |
input |
object | No | Data merged into $data inside the script |
timeout_ms |
number | No | 100–30000, capped at the project script CPU quota |
allowed_domains |
array | No | Domains the script may fetch (empty = all blocked) |
* Provide either code or scriptId.
#Communication operations
#send_email — Send Email
Sends a transactional email. Emails count against the project's daily email quota.
| Field | Type | Required | Description |
|---|---|---|---|
to |
template | Yes | Single address or array of addresses |
subject |
template | Yes | Subject line |
html |
text | No | HTML body |
text |
text | No | Plain text body |
from |
string | No | From address |
replyTo |
string | No | Reply-To address |
template |
text | No | Template with {{key}} placeholders |
templateData |
object | No | Values for the template placeholders |
Recipients are not restricted by the operation — any valid address or array of addresses works, gated only by the daily email quota (each recipient counts once). Templates support dot-path keys ({{user.name}}); a rendered template containing HTML tags is sent as the HTML body, otherwise as text.
Output
| Field | Type | Description |
|---|---|---|
messageId |
string | Provider message id where available, otherwise a generated id |
status |
string | sent |
to |
string | string[] | Echo of the recipients |
channel |
string | email_sender_binding | email_binding | rest_api — which channel delivered |
quotaRemaining |
number | Daily allowance left after this send |
Branch on channel to detect the active transport from inside a flow, without calling /health.
Errors
Every failure is typed and carries a stable code. Quota exhaustion is always a refusal, never a silent drop — the operation raises before touching a transport, so nothing is delivered and a multi-recipient batch is never partially sent.
code |
Meaning | Retryable |
|---|---|---|
email_quota_exceeded |
This project's emails_per_day would be exceeded. Carries limit, used, requested, remaining. |
No — resets daily |
email_upstream_quota_exceeded |
The provider rate-limited or hit its own daily cap (e.g. HTTP 429). | Yes, with backoff |
email_recipient_not_allowed |
Recipient unreachable on this channel — typically an unverified destination on a channel whose sending domain is not onboarded. | No |
email_no_channel |
Nothing is configured to send with. Message names exactly what to configure. | No |
email_no_capacity |
Transports exist, but every one is out of allowance, outside its sending hours, or in a cool-off after a failure. | Yes |
email_send_failed |
Delivery failed for another reason. | Only if 5xx |
email_quota_exceeded fires against the EmuView per-project quota; email_upstream_quota_exceeded fires against the provider's. Distinguish them before retrying — the first cannot succeed again today, the second usually can.
Deliverability depends on the instance's email channel. Probe GET /health → email_capabilities.arbitraryRecipients, not email_channel alone: a configured channel is not necessarily a channel that can reach your users. The Cloudflare channels (email_binding, rest_api) deliver only to verified destination addresses until a sending domain is onboarded to Cloudflare Email Service, and they share one account quota. For app-style mail to arbitrary users, configure an EMAIL_SENDER service binding fronting an email provider — send_email prefers it automatically, no flow changes needed. See Self-hosting → Deployment → Email delivery.
#send_notification — Send Notification
Sends an in-app notification to a user or the project log.
| Field | Type | Required | Description |
|---|---|---|---|
title |
template | Yes | Notification title |
message |
template | Yes | Notification body |
type |
enum | No | info (default), success, warning, error |
userId |
template | No | Target user (defaults to the flow runner) |
link |
template | No | Link opened when the notification is clicked |
#System operations
These operations only run inside system-managed flows. They appear here for completeness — you can't add them to your own flows.
| Operation | Purpose |
|---|---|
system_archival |
Archives Cloudflare GraphQL metrics to R2 for historical analysis |
system_limit_check |
Checks current usage against plan thresholds and creates alerts |
system_monthly_summary |
Aggregates daily R2 archives into a monthly summary |