BitelioBitelio

Workflows

Automated multi-step journeys for contacts, started by events, segments, schedules, or by hand

A workflow is an automation modelled as a graph: contacts advance step by step — receiving emails, pausing, taking conditional branches, hitting webhooks, having their data updated, or exiting. Every contact travels the graph on their own, in an independent workflow execution.

Triggers

Each workflow has exactly one trigger, which determines how contacts get in:

Trigger typeWhen it fires
EVENTWhenever a matching event lands on a contact — a custom event from /v1/track, a system event like email.received or contact.subscribed, or a segment entry/exit event.
MANUALNever on its own; only when you start an execution yourself via POST /workflows/:id/executions.
SCHEDULEOn a recurring cadence described by triggerConfig (a cron expression, for instance).

Workflows created through the API default to an EVENT trigger using the event name you supplied. Switching to MANUAL or SCHEDULE is done afterwards, by updating triggerType and triggerConfig with PATCH /workflows/:id.

Once a workflow has run at least once, its trigger event name is frozen — choose it deliberately. Everything else about the configuration (step delays, in-step conditions) stays editable.

Lifecycle and the enabled flag

Every workflow starts life with enabled: false, and nothing executes until you set enabled to true. If a workflow seems to never fire, check this flag first — it's the usual culprit.

Whether a contact may pass through the same workflow twice is governed by allowReentry (off by default). Keep it disabled for one-time sequences like welcome or onboarding emails; enable it when the journey should be repeatable, such as recurring nudges or re-prompts.

Step types

The first step of any workflow is a TRIGGER step, created automatically. Everything after it is built by adding steps and wiring them together with transitions (the graph's edges).

Step typePurposeRequired config
TRIGGERThe auto-created entry point, carrying the trigger configuration.eventName (for EVENT trigger)
SEND_EMAILEmails the contact from a template; variables are filled from contact data plus the execution context.templateId, optional from override
DELAYHolds the execution for a set amount of time before moving on.amount, unit (minutes / hours / days)
WAIT_FOR_EVENTHolds the execution until a given event is tracked on the contact, falling through on timeout.eventName, timeout (seconds)
CONDITIONSplits the path based on contact or event data; every CONDITION step has two outgoing transitions labelled yes / no.A filter expression (same shape as segment filters)
WEBHOOKPOSTs contact and execution context as JSON to an external HTTPS endpoint. {{variables}} work in url, header values, and body.url, optional method, headers, body
UPDATE_CONTACTApplies a patch to the contact's data — a common way to tag progress ({ stage: "activated" }).data object
EXITEnds the execution, optionally noting an exitReason for later analysis.optional reason

Transitions

The edges connecting steps are called transitions. Most of the time a transition just means "go here next"; on CONDITION steps, each outgoing transition also carries a branch value ("yes" or "no") that tells the engine which way to route once the condition is evaluated.

Removing a step from the middle of a chain? Add ?splice=true to DELETE /workflows/:id/steps/:stepId and the neighbouring transitions are stitched back together automatically — without it, the graph is left with a gap.

Executions

A WorkflowExecution is created for every contact that enters. Its possible states:

StatusMeaning
RUNNINGA step is being processed, or is about to be.
WAITINGHeld inside a DELAY or WAIT_FOR_EVENT step.
COMPLETEDMade it to the end of the graph without issues.
EXITEDReached an EXIT step; the cause is stored in exitReason.
FAILEDStopped by an unrecoverable error — for instance a webhook still non-2xx after retries, or a missing template.
CANCELLEDTerminated by hand through the API or dashboard.

Every execution has its own context JSON object. During template rendering and condition evaluation, it's merged into the contact's data. Manual starts via POST /workflows/:id/executions accept an initial context, which is the mechanism for per-execution variables — a coupon code, a referrer's name — that shouldn't be persisted on the contact itself.

Locking active workflows

While any execution is active (RUNNING or WAITING), the workflow is locked against structural changes. Before making a breaking edit — adding or removing steps, changing the trigger event, switching trigger type — do one of the following:

  • Turn the workflow off and let the in-flight executions finish, or
  • Call POST /workflows/:id/executions/cancel-all to abort everything currently running.

Non-structural edits — renaming the workflow, changing the content of templates its steps reference — are allowed at any time.

Common patterns

  • Welcome series — trigger on the signed_up event, send the welcome email, wait 2 days, send a tips email, wait 5 more days, then nudge toward an upgrade.
  • Inbound auto-reply — trigger on email.received, use a CONDITION checking event.spamVerdict == "PASS", then SEND_EMAIL with the auto-reply template. See Receiving emails.
  • Re-engagement — start from a segment exit (segment.active-users.exit), send a "we miss you" message, wait up to 7 days for an email.opened event, then branch on whether they engaged.
  • Webhook fan-out — trigger on purchase.completed, hit your CRM with a WEBHOOK step, tag the contact via UPDATE_CONTACT ({ tier: "customer" }), and finish with a SEND_EMAIL receipt.

API reference

Everything runs through the /workflows API, which covers the workflow itself plus its steps, transitions, and executions. The full route list is in the API overview.

What's next