Skip to main content

Workflow

Overview​

Workflow is a drag-and-drop automation builder on a canvas, for multi-step sequences a single rule can't express — welcoming new members, drip content, win-back sequences for quiet customers, or follow-up messages after a campaign click. One workflow is a graph that starts at a trigger node, branches through condition / delay / split nodes, and ends at an action node. There are 30 node types in total, grouped into 7 palette sections in the left rail (Triggers, Conditions, Actions, Flow Control, AI, Apps, Utility).

The one mechanic to understand before you start: a workflow doesn't run as a graph. When you activate it, the system "compiles" the whole graph flat into Trigger Rule rows — one row per path from a trigger to an action — and the background worker executes those rules. Once you grasp that, most of Workflow's surprising behavior explains itself.

Who uses it — OA admins designing multi-step automation flows (this menu has a permission caveat that differs from every other menu in the system — read Access control before using it in production).

User journey​

1. Review the workflow list​

Open Workflow. The list is sorted newest-created first, with no search box and no status filter — pagination only. The table shows Name, Status (Draft / Active / Inactive), Nodes (the node count in the graph), Last Modified, and 5 row buttons — Activity Logs / Edit / Clone / Activate-Deactivate / Delete.

2. Start building — from scratch, from a template, or with AI​

  1. From scratch — drag nodes from the left rail onto the canvas and draw edges to set the order. (The toolbar also has a Pan mode / Select mode toggle, and an Export as PNG button for saving a picture of the graph.)
  2. From a template — click Use Template and pick from 17 prebuilt templates across 5 groups (Onboarding, Segmentation & Engagement, Campaign Attribution, Scheduled & Nurture, Appointment). Template names are always English — they're hard-coded rather than going through the translation system.
  3. With AI — click AI Generate, describe what you want in plain language, and the system asks AI to draft the graph. If the workflow name field is still empty, AI fills that in too.

Both picking a template and generating with AI replace the entire canvas immediately — they don't add to it, and there is no undo. Always save your existing work before pressing either button.

3. Configure the nodes​

Click a node to open the config drawer on the right. Every value you change writes straight back into the graph on the canvas — this drawer has no save button of its own. But that does not mean anything reached the database: you still have to press Save in the top toolbar.

The options in this drawer are all pulled from other menus in the system — Rich Message, Rich Menu, target groups, merge tags, Friends Track campaigns, sent campaigns and knowledge bases (see exactly what each one filters for in Related menus).

4. Save​

The toolbar's save button relabels itself by status — Save for a draft or inactive workflow, but Save & Recompile once the workflow is active, where the confirmation dialog warns outright: "Save & Recompile workflow? … Trigger rules will be updated immediately."

The first save of a new workflow navigates you into that workflow's edit page (it needs an id before the testing tools and version history can work).

5. Test before activating​

There are two testing tools, and they are usable at different stages.

  • Test Run (toolbar: Test Run) — available as soon as the workflow has an id (a draft is fine). Give a real LINE user id from this OA and the system walks every path in the graph and reports per-path results (did the conditions match / did the action succeed). There's a dry run option too — critically important: with dry run unchecked, the system performs every action for real. See Things to watch out for.
  • Chat Sandbox (toolbar: Test Chat) — simulates chatting with the flow as a user. Only works for a workflow that has already been activated and contains a "Message Received" trigger paired with an Intent Classifier. If it hasn't been activated, the sandbox just replies that no active rules were found.

6. Activate​

Press Activate. The frontend validates the entire graph first against 34 rules — errors surface as messages naming the offending node and no request is sent to the server at all. Warnings (e.g. "Some paths don't lead to an action … These paths will be ignored") are shown but do not block activation.

The rules you'll hit most: at least one trigger, and every trigger must have an outgoing edge; every node except annotations and exit nodes must be configured; condition nodes must have both the Yes and No branches connected; delay nodes need a "Completed" branch and a minimum of 1 minute, and two delay nodes cannot be chained (not yet supported); a Split test node's percentages must sum to exactly 100%; the graph must be acyclic; and every non-trigger node must have an incoming edge.

Once it passes, the server re-validates (with a much shorter set), compiles the graph into Trigger Rules, and records a version snapshot with the activate event.

7. Versions and rollback​

Click Version History in the toolbar to open the drawer listing every version (newest first, 20 per page). Pick a version to preview its graph, or roll back to restore its definition. If the workflow is active, the system recompiles the Trigger Rules to match the version you rolled back to right away.

8. Review the run history and per-node stats​

Click Activity Logs from the list (or from the canvas toolbar) to open the logs page — search, filter by status (success/failed), filter by node type, and pick a date range, alongside a per-node execution summary panel. The canvas itself also overlays counters on each node showing how many times it ran, how many succeeded/failed, and how many are still waiting.

Key details verified against the source​

Confirmed from internal/modules/workflow/{controller,service,executor,sandbox,ai}.go in line-management-cms-api-go and src/components/workflow/ in cms-web-2026:

  • Compilation turns the graph into "one Trigger Rule per path" — for each trigger, the system enumerates every path that reaches an action node and collapses everything in between into a single rule's config: condition nodes become an ordered condition list (the "No" branch gets flagged negate: true), a Split test node becomes a percentage-window condition (cumulativeStart/cumulativeEnd), a Multi-Split node becomes "every prior branch negated + this branch's condition", and a delay node becomes a delay block embedded inside the action's config, not a separate rule row. Each rule is auto-named "{workflow name} - [{trigger label}] {action label}".
  • One path compiles to exactly one action — and a second action silently overwrites the first — the compiler allows a second action in one case only: when the first is the external-API node (action_web_request), in which case the second is embedded as postAction (or, if it's an update-attribute node carrying responseMappings, merged into the first). Outside that case, chaining two action nodes back to back drops the first one from the compiled rule with no warning whatsoever — neither the frontend nor the server has a rule for this. If you need to send two messages in sequence, split them into two paths or use the advanced send-message node.
  • A path that doesn't end at an action node is dropped entirely — the compiler continues past it. The frontend surfaces this as a non-blocking warning saying those paths will be ignored, which matches the real behavior exactly.
  • Every compile soft-deletes the previous rules that weren't recreated — after building the new rule set, the system diffs it against all existing rules for that workflow and stamps deleted_date on the ones not in the new set. So hand-editing a rule in the Trigger Rule menu gets overwritten the next time that workflow is saved or activated.
  • The server's activation validation is far shorter than the frontend's — the server checks only 4 things: (1) the definition isn't empty and has both nodes and edges, (2) at least one node whose type starts with trigger, (3) at least one whose type starts with action, (4) every node except annotation/exit has a config that isn't an empty object (it names the unconfigured nodes back to you if not). Cycle detection then happens during compilation. The other 30 rules exist on the frontend only — anyone calling POST /workflows/:id/activate directly bypasses all of them.
  • Saving a draft only validates the name — validateWorkflowForSave checks nothing but a non-empty name, so an incomplete graph can always be saved. But if the workflow is already active, pressing Save runs the full 34-rule validation just like activation, and the server recompiles the rules in that same request.
  • Test Run with dry run unchecked is real execution, not a simulation — the executor calls executeAction directly for the resolved action type: it sends the message to the real user, switches the real user's Rich Menu, writes attributes for real, adds/removes real target-group members, and fires the HTTP request at your configured URL for real, from cms-api itself (timeout configurable, default 30 seconds). Dry-run mode reports only whether each path matched, with result dry_run, performing no action.
  • Test Run always skips delay nodes — in dry-run mode it labels the result "Delay node skipped in test run", but in real mode the delay node is simply ignored and actions after the delay fire immediately. Test Run results therefore don't reflect the flow's real timing.
  • Test Run supports only 6 of the 10 action types the compiler handles — the executor knows send_message, switch_rich_menu, add_to_audience, remove_from_audience, update_attribute, web_request. Newer nodes (advanced send message, talk to agent, and the booking-app actions) come back as failed with "Unknown action type: …" even when configured perfectly and working fine in production. Read a Test Run result on those nodes as "not testable yet", not "misconfigured".
  • Chat Sandbox reads the compiled rules, not the canvas graph — it queries trigger_rule for source_type = 'message_received' AND enabled = true AND deleted_date IS NULL and, finding none, replies with the literal text "No active message trigger rules found. Please activate the workflow first." So you cannot sandbox-test unsaved or not-yet-activated canvas edits.
  • Version history only moves forward, and nothing is ever pruned — a snapshot is written on every save that carries a definition (event save), on every activation (activate), and on every rollback (rollback). The version number is always max+1 — so rolling back to version 3 creates a new version (say 12) holding version 3's definition; versions 4–11 are not deleted, and there is no automatic pruning of old versions at all.
  • Per-node stats deliberately include soft-deleted rules — the rule query in GetStats has no deleted_date IS NULL condition (a source comment confirms this is intentional), so a node's cumulative run counts aren't reset every time you recompile.
  • The plan quota is only checked at the moments that make a workflow active — creating with status active, editing to status active, and pressing Activate. It counts workflow WHERE status = 'active' scoped per LINE OA (a quota of -1 means unlimited). Over the limit returns a 400 with "Plan limit reached: you are using {x} of {y} workflows. Please upgrade your plan." — drafts are unlimited.
  • Every AI feature requires that OA's AI configuration first — AI flow generation, AI parsing of a target API's JSON response, and the sandbox's intent classifier all need a row in ai_config matching line_oa_id + organization_id + provider = 'gemini' + status = 'active', or you get a 400: AI configuration not found for provider "gemini". Please configure an API key. Only one provider is supported — Gemini (default model gemini-2.0-flash). Flow generation uses temperature 0.7 with an 8192-token cap; JSON-response parsing uses 0.3 with 4096.
  • A sub-1-minute delay in an active workflow fails compilation with a 500, not a 400 — convertToSeconds throws a plain error below 60 seconds and the compiler wraps it as an Internal Server Error. The frontend normally catches this with its 1-minute rule, but if you see a 500 while saving an active workflow, check the delay values before anything else.
  • Keyword-intent vector generation is best-effort and all-or-nothing — when compiling a message_received trigger, the system posts each intent's combined text to an external embedding API (defaulting to a proof-of-concept host via EMBEDDING_API_URL). If any one call fails, the system logs a warning and abandons the whole loop, saving no vectors at all — not even the ones already gathered. Semantic matching then works less accurately while compilation still reports success.

Every item below is verified from the requests workflow-form.container.tsx actually fires to populate the node config drawer:

  • Rich message — the option list for the send-message and advanced-send-message nodes. Only Active items are fetched (status=active, dropdown format).
  • Rich menu — the option list for the switch-menu node. Only items that are Active and flagged isForAutomation=true are fetched — a Rich Menu not marked for automation use never appears as an option.
  • User Attribute Setup — the source of the whole merge-tag list (fetched including system attributes, because the external-API node needs them) and the target of the update-attribute node. The config drawer also has an inline "create attribute" flow so you never have to leave the page.
  • Segment audience — fetched as two different lists: all target groups, used by the audience-entry/exit triggers and the membership condition node; and a list filtered to file-built groups only (dataSource: 'csv'), used by the add-to / remove-from audience nodes — so auto-condition groups never appear in those two actions, because their membership can't be edited by hand.
  • Friends Track — the Friend Track event trigger node offers Active-status campaigns only.
  • Campaign — the campaign-click trigger node uses the same dropdown endpoint the Segment audience menu does, which filters to campaigns that are sent, and sent within the last 3 months (up to 100 items).
  • Knowledge base — the knowledge-source options for the intent classifier node, used to answer questions from stored content.
  • Trigger Rule — the destination of workflow compilation. Rules carrying a workflow_id are system-generated and should not be edited by hand.

Things to watch out for​

  • Activating from the list page bypasses all 34 frontend validation rules — the row toggle calls POST /workflows/:id/activate directly, leaving only the server's 4 checks. And on failure the list shows just the headline "Failed to activate workflow" without displaying the server's explanatory message at all (the code passes desc: ''). If activation fails and you want to know why, go into the canvas page and activate from there — that page does surface the server's message.
  • Test Run without dry run sends real messages to real users and calls real endpoints — putting a real customer's LINE user id in and running means that customer really receives the message, their Rich Menu really changes, and their attributes really get written. Always use a team test account, and choose dry run when you only want to see which paths match.
  • Chaining two action nodes silently drops the first — except when the first is the external-API node. Neither the frontend nor the server warns. Verify by checking how many rules were created in the Trigger Rule menu after activating.
  • Templates and AI replace the whole canvas — no undo, and neither saves for you. Close the page without pressing Save and everything AI drafted is gone.
  • AI drafts are not saved — you always have to press Save yourself afterwards.
  • The plan quota gives no advance warning — you can build indefinitely and only get rejected at the Activate button, even with a perfectly complete flow.
  • Deactivate and delete treat the compiled rules differently — deactivating sets enabled = false on every rule but doesn't delete them (reactivating recompiles over the top); deleting the workflow sets enabled = false and soft-deletes the whole rule set.
  • Two nodes are in inconsistent states in the source — trigger_tracking_event exists in the node-type set and is registered on the canvas but does not appear in the left palette (so you can't place a new one, though older workflows containing it still work). Conversely, the "talk to agent" node is in the palette and has a full config panel but isn't registered in the canvas's node-type map.
  • Two chained delay nodes are unsupported — this is a real limitation, not a bug; there's an explicit frontend rule blocking it.
  • The menus a workflow compiles against have to keep existing — delete a Rich Message or target group a node references and the compiled rule still holds the old id; it will fail at run time (visible as a failed status on the activity-logs page).

Access control (verified against the source)​

  • Frontend — the PermissionGuard on all three pages is commented out. src/app/workflow/page.tsx, form/page.tsx and logs/page.tsx still import PermissionGuard but export the component bare, with a comment saying exactly why: // Temporarily bypassing permission check for development. So any logged-in user who types the URL directly can open all three pages, even without permission to the Workflow module. What still works is hiding the left-menu entry, which continues to check ability.can(VIEW, workflow) — that hides it from view, it doesn't keep anyone out.
  • Backend — all 18 routes in this module are wrapped in a real, enforced ModuleGate("workflow") (onemoby callers bypass it). In practice this is the only barrier that actually protects Workflow — if a platform admin closes the module for an organization, its users can open the pages but every request returns 403 and the screen stays empty.
  • Action-level policy annotations are declared on every route but run through the same no-op middleware as the rest of the system — the real enforcement is the module-level ModuleGate, and nothing else. One route is declared inconsistently: POST /workflows/:id/test-chat declares its policy as read on the lineOa module rather than workflow (no practical effect since policies are a no-op, but worth knowing).
  • Data access is scoped by the caller's token line_oa_id — Chat Sandbox double-checks this and returns a flat 403 Access denied if the requested workflow doesn't belong to both the caller's OA and organization.
  • DELETE /api/workflows/:id is a soft delete that also disables and soft-deletes the entire compiled rule set — there is no hard-delete route and no Super Admin check in this module at all.

Source file reference​

Frontend (cms-web-2026)

  • src/app/workflow/page.tsx, form/page.tsx, logs/page.tsx — all three page entry points, and where PermissionGuard is commented out
  • src/components/workflow/list/workflow-list.container.tsx — the table, the status toggle (where validation is bypassed), clone and delete
  • src/components/workflow/form/workflow-form.container.tsx — save/activate/deactivate/rollback, and every request that pulls options from other menus
  • src/components/workflow/form/workflow-form.tsx — the toolbar, where validation is invoked, template selection and AI generation (where the canvas is wholesale replaced)
  • src/components/workflow/form/utils/workflow-validation.ts — all 34 validation rules and the dead-end-path detection
  • src/components/workflow/form/components/node-palette.tsx — the 7 node groups and their contents
  • src/components/workflow/form/components/node-config-drawer.tsx — the config panel for every node type (writes straight back into the graph with no save button of its own)
  • src/components/workflow/form/components/test-run-modal.tsx, chat-sandbox.tsx — both testing tools
  • src/components/workflow/form/components/version-history-drawer.tsx, version-preview-modal.tsx — the version history drawer and preview
  • src/components/workflow/form/data/workflow-templates.ts — all 17 templates
  • src/components/workflow/logs/workflow-logs.container.tsx — the activity-logs page and its filters
  • src/services/types/workflow.type.ts — the full set of 30 node types

Backend (line-management-cms-api-go)

  • internal/modules/workflow/controller.go — all 18 routes and ModuleGate("workflow")
  • internal/modules/workflow/service.go — create/update/delete/clone logic, activation validation, the compile function that turns the graph into Trigger Rules, version snapshots and rollback, stats and logs, and the AI calls
  • internal/modules/workflow/executor.go — the Test Run executor and the executeAction switch covering 6 action types
  • internal/modules/workflow/sandbox.go — Chat Sandbox (confirming it reads the compiled rules)
  • internal/modules/workflow/ai.go, prompt.go — the Gemini call via the ai_config table and the flow-generation prompt
  • internal/modules/workflow/paths.go, helpers.go — graph path enumeration and cycle detection
  • internal/modules/planlimits/service.go — the active-workflow quota check

See the documentation team's earlier technical write-up at Workflow Automation