Skip to main content
A definition is the blueprint for a flow. It declares what data the flow accepts, the steps it performs, and how it reacts to success and failure. You create definitions with POST /flows/definitions and run them with POST /flows/definitions/{id}/trigger — see how GetDialed works for the big picture.

Top-level structure

Expression syntax is validated when you create or update a definition — a malformed expression returns 422 with the offending step, task, and field.

Inputs

Inputs declare the data a caller provides in input_data when triggering. Each input has:

Searchable inputs

Support work starts from a business key — a lead id, a phone number, a list name — not from an execution id. searchable_inputs names up to five of a definition’s inputs whose values should be recorded on every run, so runs can be looked up by them afterwards.
An entry is either an input name or a dotted path into one, which is how a per-record flow makes a field of each record searchable. The query key is the path lowercased, with dots replaced by underscores. customer.phone is filtered as attr.customer_phone; lead_id is filtered as attr.lead_id. See finding a run by business key for the filter itself. A few rules are worth knowing before you declare one:
  • Values are captured when the run starts and never change. Editing searchable_inputs afterwards changes what future runs record; it does not rewrite what past runs were found by. A run’s search keys describe what it started with.
  • Phone-shaped values are stored in international format. A run started with (707) 634-2533 is found by attr.phone=%2B17076342533, and so is one started with +1 707 634 2533. A value that cannot be read as a phone number is stored exactly as it arrived, so it is still findable.
  • Values longer than 256 characters are shortened, and values that are not simple — an object or a list — are not recorded at all, because there is nothing to match them against exactly.
  • Naming an input the definition does not declare is rejected with 422 when you save the definition, as is declaring more than five, or two entries that produce the same key.

Variables

Variables are definition-level values — thresholds, list names, feature switches — referenced as {{var.name}}. Each has a name, a type (same set as inputs), and a value. Changing a variable means updating the definition, so use them for values that change per version, and use inputs for values that change per trigger.

Steps and tasks

Steps run sequentially; the tasks inside a step run concurrently. Each step has a step_id (unique within the definition), an optional name, a list of tasks, and optional exit_conditions. Every task requires five identifying fields: And supports these optional fields:
task_id values must be unique across the entire definition — not just within their step — and step_id values must be unique too. Duplicates are rejected at creation time.

Success and failure policies

  • on_failure: abort_step (default) — the task’s error fails the execution.
  • on_failure: log_and_continue — the task records a failed result and the flow proceeds.
  • on_failure: retry — the task is attempted again before the flow gives up. See retrying a task.
  • on_success: skip_step — a successful result skips the remaining tasks in the current step.
  • on_success: abort_execution / on_failure: abort_execution — stop the whole execution.

Retrying a task

retry_policy says how a task re-attempts itself after a failure. It applies only when the task’s on_failure is retry; declaring a policy without it is rejected when the flow is saved. What transient means. Every action reports whether a failure was temporary — a timeout or a refused connection reaching the provider — or final. retry_on: transient retries only the temporary ones. retry_on: any also retries failures the action did not classify. Neither ever retries a failure the action reported as final: if the action says the work will not succeed on a second try, it is believed. Not every action can be retried. Every action in the catalog declares how safe it is to repeat, and that declaration bounds what you can ask for:
  • An action that must never be repeated refuses a retry policy outright. Saving the flow returns a 422 naming the action.
  • An action that must not be repeated blindly accepts a policy only with retry_on: transient.
  • An action that is safe to repeat freely accepts any policy.
The reason is that repeating a call which may already have reached the provider can duplicate real work — sending a message twice, or dialing a consumer twice. The bound is also checked while the flow runs, so an action that is reclassified after your flow was saved stops being retried straight away. Retry a lookup that occasionally times out:
That task runs at most four times, waiting 10 seconds, then 20, then 40 — and only when the action reported the failure as temporary.
A retry policy and provider rate limits do not stack. See retry policies and pacing.

Skipping a task

skip_if puts a condition on a single task. It is evaluated immediately before the task would run, against the same context the task’s parameters see, and it uses the same syntax as a step’s exit condition — a comparison such as {{input.email}} == null, or a bare expression, which passes when it resolves to a truthy value. See conditions for the full syntax. When the condition passes, the task does not run, it is recorded as skipped, and the rest of the step still runs. That is the difference from on_success: skip_step: skip_step skips the tasks that follow a task that did run, while skip_if skips only the one task it is written on. A skipped task’s on_success and on_failure never apply — it neither succeeded nor failed. Later steps can see what happened: the skipped task’s output carries _skipped: true, so {{send.receipt.output._skipped}} resolves to true when the task was skipped. Send the receipt only when an address is present:
If input.email is absent, receipt is skipped and log still runs.

Exit conditions

Exit conditions run after all tasks in a step finish. Each pairs a boolean expression with an action:

Complete example

A two-step definition: fetch Five9 contact lists, then add a record to one of them during business hours.
Definitions start as draft. Set status to active (at creation or via PUT /flows/definitions/{id}) before triggering — triggering a draft returns 400.

Deleting a definition

DELETE /flows/definitions/{id} removes a definition. If any schedules still reference it, the delete is refused with 409 so a live schedule is never left firing into a definition that no longer exists. The response body lists the blocking schedules:
Delete each listed schedule with DELETE /flows/schedules/{id}, then retry the definition delete. Batches and executions the definition already produced are unaffected.