Reference

Recipe schema

The recipe envelope, step model, value system, condition operators, namespaces, transforms, and AI functions.

A condensed field reference for recipe authors. For the narrative introduction, read Authoring recipes first.

#Envelope

json
{
  "recipe_id": "detect-deal-risk-hubspot",
  "version": 1,
  "ttl": 600,
  "metadata": {},
  "variables": {},
  "requires": [],
  "depends_on": [],
  "chat_exposed": false,
  "trigger": [],
  "event_triggers": [],
  "webhook_triggers": [],
  "auto_run": null,
  "trigger_steps": null,
  "run_mode": null,
  "provenance": true,
  "prefetch_steps": [],
  "steps": [],
  "output": {}
}
Field Type Notes
recipe_id string {action}-{entity}-{qualifier}[-{platform}], platform always last
version number Integer version of this recipe
ttl number Default cache TTL in seconds for ingredient fetches
metadata object Display and marketplace fields (below)
variables object User-configurable values, read as {{config.*}}
requires string[] Permissions the recipe needs, such as notification_send or mail_send
depends_on string[] Packs whose operations the steps call, as publisher.pack; built-in core.* operations need no entry
chat_exposed boolean Offer this recipe to chat and connected agents as a tool; see Exposing recipes to chat
trigger string[] URL patterns the recipe is designed for; absent means manual-only
event_triggers object[] Warehouse events that fire the recipe; see Triggers and watchers
webhook_triggers object[] Inbound webhooks that fire the recipe, each named with a verification profile
auto_run object Reactive regime: { "interval_ms": 60000, "dynamic": false }
trigger_steps step[] Gate phase before each reactive tick; requires auto_run
run_mode string live, backfill, or manual; tags audit entries
provenance boolean Default true; emits the links that power timelines
prefetch_steps step[] Fetched in parallel before sequential steps
steps step[] Sequential work
output object render sections routing step results to display

#metadata

Field Notes
name, description Display
author Publisher handle
tags Free-form strings for discovery and filtering
supported_platforms Platforms this variant targets
variant_group Groups cross-platform variants of one recipe family
execution_scope Optional narrowing, a subset of the scope derived from ingredient kinds
budget_ms Wall-clock cap for a run; the engine aborts past it. Set one on everything you ship
fork_of Lineage of a published fork: original recipe, author, version

#Steps

json
{
  "id": "risky",
  "transform": "filter",
  "array": "{{step.deals}}",
  "field": "stage",
  "operator": "equal",
  "value": "at_risk",
  "skip_when": "{{step.deals}} is_empty"
}

The step type is inferred from which field is present:

Kind Field What it does
Transform transform Pure local function; its parameters sit flat on the step — see Transforms
Ingredient ingredient Declared operation: HTTP, DOM, AI, chat, MCP, service, storage, or connection; parameters under input, with dotted keys such as llm.data
Operation op A named operation — built-in (core.mail.get, core.notification.send, core.watch.time, …) or from an installed pack (publisher.pack.entity.verb, listed in depends_on). Arguments under args; the bound account under connection, usually "{{config.<your connection variable>}}"
Guard guard The field is a condition string; halts the recipe when it is met
Condition condition Evaluates a condition and routes into then / else step blocks

Execution flow, identical for every step:

text
skip_when? → compute → step.<id> = result → fail_on?
  • skip_when true → the step stores null and the recipe continues.
  • fail_on true (evaluated after compute) → the recipe halts.
  • foreach iterates an array input, binding {{item.*}} per iteration; the step's output is one envelope per element, { ok, result?, error?, item }, in source order.
  • Prefetch steps additionally accept "optional": true — a failure stores null instead of halting the run.

Trigger-phase steps (trigger_steps) use the same shape. Every one must return should_run: true for the reactive tick to proceed; their outputs land on {{trigger.<id>.*}} for downstream steps.

#Variables

Two forms. A bare value is a constant you tuned; its type is inferred and the one-shot run form leaves it alone. An object is a question for whoever runs the recipe, and gets its own field on that form.

json
"variables": {
  "recipient":  { "label": "Send to", "type": "text" },
  "window":     { "label": "Window", "type": "enum", "options": ["7d", "30d"] },
  "max_rows":   50,
  "deal_id":    null
}
Bare value Inferred Control
50 number Number input
true / false boolean Toggle
"text" string Text input
["a", "b"] enum Dropdown, first element is the default
null string, required Text input with no default; the recipe cannot run until it is supplied

A tuning constant stays editable everywhere tuning belongs — the install dialog, the recipe editor, and the Schedule and Trigger tabs, where "run nightly with window: 30d" is legitimate setup. Only the one-shot run form stops asking.

#Object-form fields

These are the only fields that exist. Anything else is refused when you install, so an invented field fails loudly instead of being quietly ignored.

Field Notes
label Required. The text shown, and the signal that this is a question rather than a constant
type Required, and a non-empty string. See the table below
optional true lets the runner leave it empty. Leave it out to require a value
default Used when nothing is supplied
help One line under the control
link A documentation URL for the control
options type: "enum" — the choices; the first is the default
provider, scopes type: "oauth" — broker routing and requested scopes
accept_mime_types type: "file_ref" — narrows the picker only; never a security boundary
connection_kind type: "connection"api, mcp, or notification
variant_group type: "service_ref" — which enrolled service family the picker offers

There is no required field: optional is the one that exists, and leaving it out already means required. There is no pattern either — validate the shape of a value with a guard step, since a declaration says what a value is called and how it is collected, never what counts as valid.

#Types

Unlike the field list, type is open. These drive a specific control:

text · number · boolean · enum (with options) · datetime · url · secret · oauth · file_slug · file_ref · file_ref[]

These are admitted too and are widely used. connection and service_ref get their own pickers; the rest render as a plain text control:

string · array · object · json · connection · service_ref

string is by far the most common type in shipped recipes and behaves as text. Any other non-empty string installs and renders as text, so the set can grow without invalidating what you have already published — which is exactly why an unknown type is admitted while an unknown field is not. A type has a sensible fallback; a field nobody reads has none.

#A caller may only pass what you declared

If a chat request, an MCP call, a button, or a schedule overlay hands your recipe a name that is not in variables, the run is refused rather than ignoring it. So every argument you want to accept has to be declared, and a leftover name you no longer read has to go.

Give a default only where a sensible one exists. A required input with a placeholder default — an empty string, a zero, an empty list — is not required at all: the caller can omit it and the recipe runs with the placeholder.

#Value system

text
"literal"              → use as-is
"{{ref}}"              → resolve, preserve type
"{{ref:format}}"       → resolve; the hint applies only during interpolation
"text {{ref}} text"    → interpolate → string
10, true, false        → native JSON type
null                   → required from caller

Format hints: :currency, :number, :date, :relative, :percent.

#Conditions

Inline strings, one condition per field, one operator per condition. Literals are bare — equal closed_won, never equal 'closed_won'.

Operator Form
equal, not_equal binary
greater, greater_or_equal, less, less_or_equal binary
is_null, is_not_null, is_empty, is_not_empty unary
contains, not_contains binary
in, not_in binary, array value — use the object form

Object form for array values:

json
{ "field": "{{step.x}}", "operator": "in", "value": ["a", "b"] }

Compound logic is a transform step (any / all) whose stored boolean you check; and / or do not exist inside condition strings.

#Namespaces recipes can reference

Namespace Contents
config Recipe variables, set at install or run time
context Caller state, plus engine-injected fields (below)
meta Recipe metadata
step All step outputs, prefetch and sequential
trigger Trigger-phase outputs on reactive recipes
item The current foreach element
shared User-writable cache-tier records
data.shared User-writable durable records
data.memory Audit and memory entries; requires the read-memory permission
data.contact The contact graph, keyed by canonical email
connection Non-secret fields of named connections

{{vault.*}} is ingredient-only — recipes never see credential material. Vendor read-side aliases (data.hubspot.*, data.salesforce.*, data.crm.*) resolve enrichments on CRM records, with static ids only.

#Engine-injected context

Field Meaning
context.tabs Ingredient slugs whose URL patterns match open browser tabs
context.server { available, name? } — paired-server reachability
context.event Trigger payload on reactive fires; payload.prev snapshots prior hot fields on updates
context.recipe.* Prior-run step snapshots for run-to-run continuity; undefined on the first run

#Transforms

Per-transform parameters live in the transforms reference. Tier 1 — atomic:

Group Transforms
Collection filter, sort, map, reduce, unique, flatten, slice, group_by, to_list, partition
Object merge, pick, omit, rename, set
String lowercase, uppercase, trim, split, concat, replace, template, truncate, strip_html
Numeric round, clamp, to_number, math, weighted_score
Date date_diff, date_format, date_add, date_parse, is_past, is_future, date_period
Logic compare, coalesce, switch, all, any, count, default, not, ternary, pluralize
Boolean starts_with, ends_with
Privacy hash_replace, hash_restore, redact
Display to_checklist, to_table, to_summary, to_csv

Tier 2 — compound conveniences: find, pluck, sum, min_by, max_by, percent, join, group_by with aggregate.

Notable modes: filter accepts mode: "all" | "any" with a conditions array for multi-condition filtering in one step; map accepts an expression over {{item.*}} (a single ref, a math expression, or an object template), with output_field to attach the computed value to each item instead of replacing it.

#AI functions

Function Input Output
ai-classify llm.data, llm.categories[], llm.context? { category, confidence, reasoning }
ai-score llm.data, llm.criteria[], llm.scale? { score, breakdown[], reasoning }
ai-extract llm.data, llm.fields[] { [field]: value }
ai-summarize llm.data, llm.max_length?, llm.focus? { summary, key_points[] }
ai-sentiment llm.data { sentiment, score, signals[] }
ai-compare llm.data_a, llm.data_b, llm.dimensions[]? { differences[], similarities[], recommendation }
ai-generate llm.data, llm.template_type, llm.tone? { content }
ai-translate llm.data, llm.target_language { translated, source_language, confidence }
ai-rewrite llm.data, llm.style, llm.instructions? { rewritten }
ai-prompt llm.system_prompt, llm.prompt, llm.output_format?, llm.model_hint?, llm.allow_search? uncontracted

Batch mode: every function except ai-compare accepts an array llm.data plus a non-empty llm.id_field — one model call, returning a per-element array in input order, each element the source record merged with the result fields. Model hints: fast, quality, thinking.

#Output

json
"output": {
  "render": [
    { "type": "summary", "source": "step.overview", "label": "Overview" },
    { "type": "ai_analysis", "source": "step.analysis" }
  ]
}

Pure routing, no logic — a null source is not rendered. Section types:

Type Renders
checklist Output of to_checklist as pass/fail/neutral items
table Output of to_table as a data table
summary Output of to_summary as key-value pairs
ai_analysis An AI result with its confidence badge
text Any string
copyable A string with a copy affordance
button Owner-clicked actions that open a normal, still-governed recipe run
file_artifact One or more file cards with authenticated preview and download
link_button A plain outbound link — label plus HTTPS URL; the one block a public visitor-facing page can act on
json Raw structured data from a step — the detail behind a curated card. Each surface decides how to show it: the web surfaces collapse it, a chat or agent caller receives it verbatim
filter An editable form built from your declared variables; submitting re-runs the whole recipe with the collected values. Owner-only — a visitor-facing page omits the block entirely

render is the only channel by which a run's data reaches any reader. A step result that is not routed into a section is unreachable — by a person, and by a model. Reach for json when a reader needs to check the work behind a summary.

The filter block is the one section that is not { type, source, label }. It also carries fields (declared variable keys shown as controls), hidden (declared keys carried on submit but never displayed, a paging cursor being the usual case), and submit (the button text). Every control is derived from the variable's own declaration, so there is no per-field styling to write.

#When Recued refuses your recipe

Validation names what it rejected. The ones authors meet most:

Refusal Cause
output_unknown_key, output_section_unknown_key A field invented on output or on a section. label is the display field; there is no title, content, or body
variable_hint_invalid An object-form variable missing a non-empty label or type
variable_hint_unknown_key A field on a variable that does not exist — most often required, which was never one
undeclared_variable_ref The recipe reads {{config.x}} with no x in variables
undeclared_config_argument A caller supplied a config key the recipe never declared. Raised at run time, not install
unknown_transform A transform name that is not registered
condition_operator_invalid An operator outside the fourteen
forward_step_ref A step reads a step that runs later
nested_template A {{ }} inside another {{ }}
context_recipe_in_auto_run {{context.recipe.*}} in a reactive recipe. It carries prior-run state on manual and scheduled runs only
filter_variable_undeclared, filter_field_not_labeled A filter block naming a key that is not declared, or one that is a bare constant rather than a labelled question

orphan_step and ttl_below_floor are advisory rather than refusals: a step nothing reads, and a cache TTL below the practical floor.

#Naming

text
Recipe:      {action}-{entity}-{qualifier}-{platform}    detect-deal-risk-hubspot
Ingredient:  {action}-{entity}-{platform}                deal-reader-hubspot
AI:          ai-{function}                               ai-score
Vendor:      {action}-{vendor}                           search-exa

Platform always last — this is what lets variant_group collapse cross-platform families into one marketplace card.

Recued is local first; your server remains the authority.

Recued Docs

Search documentation

Start typing to search the documentation.