Workflow

Complete guide to Workflow graphs, versions, runs, groups, goals, exits, agent tools, publishing, and verification.

Workflow is Lumail's graph-based automation engine. Use it when a sequence needs branching, event waits, weighted splits, conversion goals, global exits, mutually exclusive groups, immutable published versions, dry runs, or a detailed execution timeline.

Use this guide

If you want to...Read
Build your first graph in the editorGetting started
Choose enrollment events and filtersTriggers
Enroll a filtered list by handManual enrollment
Add conditions, event waits, or loopsBranching
Compare two or more variantsA/B testing
Define and measure conversionGoals and success goals
Stop runs when subscriber state changesExit rules
Understand drafts and immutable versionsPublishing and versions
Test safely and inspect resultsTest runs and timelines

Workflow editor showing a complete graph and its configuration rails

Mental model

Every Workflow has four related layers:

LayerPurpose
WorkflowOrganization-owned name, status, repeat settings, group assignment, exits, and current draft
Draft definitionEditable complete { steps, edges } graph; changing it does not change existing runs
Published versionImmutable snapshot created from the draft; every publish increments the version
Subscriber runOne subscriber executing one published version with its own status, current step, schedule, context, metadata, and timeline

Existing runs remain pinned to the version they started with. Editing or publishing a new draft never rewrites an existing run.

Workflow and run statuses

Workflow statuses:

StatusMeaning
DRAFTNever published; editable but cannot enroll subscribers
ACTIVEPublished and eligible for new enrollment and execution
PAUSEDPublished but new execution is paused until reactivated
ARCHIVEDRemoved from normal operation while preserving versions and history

Subscriber run statuses:

StatusMeaning
ACTIVEExecuting or ready to execute a step
WAITINGWaiting for time, an event, audience changes, or sending access
COMPLETEDReached the end, a goal, or an explicit exit
CANCELEDStopped by an exit rule, group conflict, manual removal, or lifecycle action
FAILEDStopped by an unrecoverable execution failure

Run data includes workflowVersionId, currentStepId, nextActionAt, runNumber, isDryRun, waitingForAudienceChange, context, metadata, lifecycle timestamps, and goalReachedId. Timeline events record step starts/completions, decisions, waits, dry-run skips, exits, goals, and failures.

Graph contract

The definition is a complete graph:

{
  "steps": [],
  "edges": [],
  "exitMode": "DISABLED",
  "exitRules": [],
  "successGoals": []
}

Validation requires:

  • Unique stable step and edge IDs
  • At least one TRIGGER
  • Every non-trigger step reachable from a trigger
  • Every edge source and target to reference an existing step
  • CONDITION steps to have YES and NO edges
  • WAIT_UNTIL steps to have MATCHED and TIMEOUT edges
  • SPLIT percentages to total 100
  • MOVE_TO_STEP to target an existing different step
  • GOAL steps to be terminal with no outgoing edge
  • Empty exit rules to remain inactive until they contain a subscriber filter

The supported edge handles are DEFAULT, YES, NO, MATCHED, TIMEOUT, and SPLIT_n such as SPLIT_1.

Trigger events

A trigger can listen to one eventName or multiple eventNames, plus subscriber audience filters and event-specific filters.

Supported events:

  • subscriber.created
  • subscriber.updated
  • subscriber.unsubscribed
  • subscriber.tag.added
  • subscriber.tag.removed
  • subscriber.field.changed
  • subscriber.email.sent
  • subscriber.email.delivered
  • subscriber.email.opened
  • subscriber.email.clicked
  • subscriber.email.bounced
  • subscriber.payment
  • subscriber.refund
  • subscriber.capture_page.submitted
  • subscriber.workflow.completed
  • subscriber.workflow.goal_reached

Event-filter keys are strict:

KeyTypeValid events
tagIdsArray of tag IDsTag added and tag removed
productProduct identifierPayment and refund
campaignIdCampaign IDEmail sent, delivered, opened, clicked, and bounced

Unknown keys are rejected. An omitted eventFilters object matches every event of the selected type.

Step types

TRIGGER

Starts enrollment from one or more subscriber events. Audience filters must match when the event is evaluated.

{
  "id": "trigger",
  "type": "TRIGGER",
  "config": {
    "eventName": "subscriber.tag.added",
    "eventFilters": { "tagIds": ["tag_123"] },
    "filters": []
  }
}

EMAIL

Sends the internal workflow email owned by that exact workflow step. The stored graph references its campaignId, but agent-built drafts should provide complete inline email content to configure_workflow_draft.

Workflow email rules:

  • Lumail creates a dedicated campaign with type: WORKFLOW.
  • One internal campaign belongs to exactly one workflow and step.
  • A broadcast or another step's campaign is duplicated, never linked directly.
  • Internal workflow campaigns do not appear in normal broadcast campaign lists.
  • The run fetches current subscriber data before rendering.
  • Sending uses the same durable bulk queue and organization quota as campaigns.
  • Sending suspension parks the run instead of discarding it.

Never call create_campaign to create a Workflow email.

WAIT

Pauses execution using one of three configurations:

  • DURATION: positive duration in minutes, hours, or days
  • TIME: next occurrence of a local HH:mm time
  • DAY: next named weekday, optionally combined with time in the editor

Organization timezone controls calendar waits. For J0/J1/J3/J5 schedules, use incremental waits of 1, 2, and 2 days.

WAIT_UNTIL

Waits for either audience filters (mode: FILTER) or a subscriber event (mode: EVENT). It can include a timeout duration and must route through both MATCHED and TIMEOUT edges.

Event mode uses the same strict event-filter keys as triggers. Filter mode wakes when subscriber data changes and the filters become true.

CONDITION

Evaluates audience filters once and routes to YES or NO.

SPLIT

Assigns subscribers to weighted branches. Each branch has a handle, percentage, and optional label; percentages must total exactly 100.

MOVE_TO_STEP

Moves execution directly to another existing step ID. It cannot target itself or a missing step.

ACTION

Supported action types:

ActionDataBehavior
ADD_TAGStagIdsAdds tags and may trigger other workflows
REMOVE_TAGStagIdsRemoves tags
SET_FIELDfieldId, valueUpdates a subscriber field
ADD_TO_WORKFLOWworkflowId, optional workflowStepIdEnrolls in an eligible workflow
UNSUBSCRIBENoneUnsubscribes the subscriber
WEBHOOKurl, optional secret, optional payloadPOSTs workflow context; payload is nested, never merged

A terminal ADD_TAGS handoff is deferred until the emitting run completes, allowing First Wins groups to chain workflows safely. Non-terminal tags are evaluated immediately.

Webhook secrets are redacted from agent reads as [REDACTED]. That placeholder copies a previously stored real secret on a later graph update. First configure, or a new webhook step, must write the real secret or omit it — sending [REDACTED] is rejected when no real secret is stored. See Webhook Step.

GOAL

Marks a reusable workflow goal and completes the run. It must not have an outgoing edge. Agent configuration accepts an existing goalId or an inline createGoal object; goal creation is case-insensitively idempotent inside the same transaction.

EXIT

Terminates one routed branch as exited and can store an optional machine-readable reason. This is different from a global exit rule that watches every run before configured step boundaries.

Success goals

Top-level successGoals watch subscriber filters and optional events while a run is ACTIVE or WAITING.

Each success goal contains:

  • Stable id and display name
  • goalId or inline createGoal
  • Audience filters (subscriber state)
  • Optional eventName and strict eventFilters (which event)
  • completeWorkflow

eventName + eventFilters select the event. filters is subscriber state. They are not interchangeable. Empty filters is fine only when a tag-added / tag-removed event also pins eventFilters.tagIds. Do not copy the trigger advice “don’t repeat the tag as an audience filter” onto success goals.

If the conversion is that the subscriber now has a tag, prefer eventName omitted plus a TAG BELONGS_TO_ANY filter. If you watch subscriber.tag.added itself, pin eventFilters.tagIds. Omitted or empty tagIds matches every tag mutation; completeWorkflow: true then completes every active run.

Filters inside one success goal use AND semantics. Goals are evaluated in array order and the first match is recorded. With completeWorkflow: false, Lumail records the conversion and keeps the run active. With true, it records the goal and completes the run.

Global exits

exitMode controls when exitRules are evaluated:

ModeEvaluation point
DISABLEDNever
BEFORE_EMAILImmediately before each email
BEFORE_EACH_STEPBefore every step

Filters inside one rule use AND semantics; multiple rules use OR semantics. A match cancels the run before the step executes. An enabled mode with no configured filters is valid but inactive: it never exits a subscriber until at least one rule contains a filter.

Repeat settings

allowRepeats controls whether the same subscriber may start the workflow again. repeatCooldownMinutes sets the minimum delay between runs and defaults to 1,440 minutes. Active-run deduplication still prevents simultaneous duplicate runs in the same workflow.

Workflow groups

Groups make workflows mutually exclusive for a subscriber. Conflicts are matched by organization, subscriber, and group membership across active or waiting runs.

StrategyLabelResult
CANCEL_EXISTINGLatest winsCancel existing conflicting runs, then start the new workflow
SKIP_NEWFirst winsKeep the existing run and skip the incoming enrollment
RUN_FIRST_STEPSend first email onlySkip the incoming run but send its first email once

Group assignment affects future enrollments. Detaching a workflow or deleting its group does not alter or cancel existing runs. See Workflow Groups for chaining and agent tools.

Drafts, publishing, and status changes

Saving the draft changes only editable state. Publishing validates the complete graph and snapshots it as a new immutable version.

  • First publish normally activates a DRAFT workflow unless activate: false is explicit.
  • Publishing an already active workflow never deactivates it.
  • Publishing a paused workflow never silently reactivates it.
  • Activation, pausing, archiving, and publishing are separate lifecycle decisions.
  • Existing runs continue on their enrolled version.

Publishing and status changes are confirmation-gated through MCP and the Tools API because activation may send emails or mutate subscribers.

Configure a complete draft with an agent

The safe sequence is:

  1. Call get_skill with { "type": "workflow" }.
  2. Call get_workflow and preserve its exact updatedAt.
  3. Discover referenced tags, fields, senders, snippets, goals, workflows, and groups with read-only tools.
  4. Call configure_workflow_draft once with complete steps and edges arrays.
  5. Read the returned definition and configuredEmails mapping.
  6. Read and render every internal email.
  7. Publish separately only after explicit human authorization.

configure_workflow_draft is atomic and never publishes, activates, enrolls, or sends. A timeout or rollback leaves the draft unchanged. Retry the same complete payload only after a fresh read and updated concurrency value.

{
  "workflowId": "workflow-id",
  "expectedUpdatedAt": "2026-07-27T10:00:00.000Z",
  "settings": {
    "name": "Welcome journey",
    "allowRepeats": false,
    "groupId": "group-id",
    "exitMode": "BEFORE_EACH_STEP",
    "exitRules": [
      {
        "id": "exit-unsubscribed",
        "name": "Customer tag present",
        "filters": [
          {
            "type": "TAG",
            "field": "tags",
            "operator": "BELONGS_TO_ANY",
            "tagIds": ["tag-customer"]
          }
        ]
      }
    ]
  },
  "steps": [
    {
      "id": "trigger",
      "type": "TRIGGER",
      "config": { "eventName": "subscriber.created", "filters": [] }
    },
    {
      "id": "email-welcome",
      "type": "EMAIL",
      "name": "Welcome",
      "email": {
        "subject": "Welcome aboard",
        "preview": "Your first steps",
        "content": {
          "type": "doc",
          "content": [
            {
              "type": "paragraph",
              "content": [{ "type": "text", "text": "Welcome!" }]
            }
          ]
        }
      }
    }
  ],
  "edges": [
    {
      "id": "edge-trigger-email",
      "source": "trigger",
      "target": "email-welcome",
      "sourceHandle": "DEFAULT"
    }
  ]
}

Use stable step and edge IDs. Stable IDs let retries update the same internal email instead of creating replacements.

Workflow tools

Organizations receive these tools through the in-app assistant, MCP, SDK, CLI, and Tools API:

ToolPurpose
list_workflowsList workflows with status, group, publication, and active-run data
get_workflowRead the complete draft, group, settings, goals, version, and concurrency value
create_workflowCreate an empty draft
configure_workflow_draftAtomically configure graph, emails, goals, and settings
update_workflow_draftReplace an existing complete graph
publish_workflowPublish an immutable version; confirmation-gated
update_workflow_statusActivate, pause, or archive; confirmation-gated
add_subscriber_to_workflowManually enroll one eligible subscriber
add_subscribers_to_workflowManually enroll a filtered set (Select all). Use dryRun: true first
remove_subscriber_from_workflowCancel active runs for a subscriber
get_subscriber_workflow_runsInspect a subscriber's runs: status, current step, nextActionAt
fast_forward_workflow_subscriberExpire the current WAIT / WAIT_UNTIL and resume now. One wait per call
delete_workflowDelete an unused draft or archive history; confirmation-gated
list_workflow_groupsDiscover groups, strategies, IDs, and workflow counts
get_workflow_groupInspect workflows, active runs, strategy, and concurrency
create_workflow_groupIdempotently create a group
update_workflow_groupRename or change strategy with optimistic concurrency
set_workflow_groupAssign or detach a workflow without replacing its graph
delete_workflow_groupSafely delete and optionally detach assigned workflows; confirmation-gated

The live schema is authoritative:

lumail tools get configure_workflow_draft --account <org>
lumail tools get create_workflow_group --account <org>
lumail tools get set_workflow_group --account <org>

Dry runs, timelines, and observability

The editor can start an isolated dry run for a subscriber. Dry runs evaluate routing and record timeline events while skipping real email and action side effects.

Use the workflow dashboard and run timeline to inspect:

  • Enrollment and run status
  • Current step and next action time
  • Branch decisions and wait reasons
  • Email/action dry-run skips
  • Goal and exit events
  • Failures and stuck runs
  • Active counts by step

Dry runs are excluded from production active-run metrics.

Verification checklist

Before claiming a Workflow change is complete:

  1. Read get_workflow again and compare updatedAt, steps, edges, settings, goals, group, and publishedVersionId.
  2. Read every configuredEmails campaign and compare sender, subject, preview, and TipTap content.
  3. Render every internal email and require non-empty HTML/text plus an unsubscribe link.
  4. Confirm internal WORKFLOW campaigns do not appear as broadcasts.
  5. Reconstruct incremental wait timing and every branch path.
  6. Read group data again after group changes and verify conflict scope.
  7. For draft-only work, prove version and publication timestamps did not change.
  8. For published work, verify the intended version and status separately.
  9. Inspect run timelines and server logs for runtime errors.