G Growreplies docs

Build your agent

Workflows

Workflows let you script how an agent responds to common visitor questions before the LLM is involved. Useful for canned answers (refund FAQ, hours, store policies), lead-qualifying questionnaires, branching qualification flows, and direct human handoff on specific keywords.

How it fits

Every visitor message runs through this pipeline:

  1. Curated answer match — admin-pinned exact-question answer.
  2. Workflow match ← this page.
  3. RAG retrieval + LLM generation.

A workflow short-circuits the LLM call when it matches: the visitor receives the scripted bubbles, no token cost is incurred, and the conversation is tagged with the workflow run for analytics.

How a workflow connects to an agent

A workflow only fires for a visitor turn when three conditions all line up:

  1. Status is active. New workflows start in draft and the runtime ignores them entirely until you flip the status — that's how you stage edits without breaking live traffic. disabled is a third state for "paused but not deleted".
  2. Agent scope matches. On the workflow's edit page, the Scoped to agent select gives you two shapes:
    • Workspace-wide (every agent)agent_id IS NULL. The workflow is eligible for every agent in your workspace. Good for cross-cutting flows like "the word 'human' anywhere triggers handoff".
    • Pinned to one agent — pick a specific agent from the dropdown. The workflow is eligible only for conversations on that agent. Good for shop-specific FAQ ("returns" only matters on the Shopify-store agent, not on the docs agent).
  3. Trigger fires. The visitor's message has to actually match the workflow's keyword set (under the configured any / all / exact match mode).

Concretely, for every visitor turn the engine runs one query:

SELECT * FROM workflows
 WHERE status = 'active'
   AND workspace_id = :current_workspace
   AND (agent_id IS NULL OR agent_id = :current_agent)

…and walks the matching rows in order, returning the first whose keywords match the message. Order is the insertion order from the database (i.e. older workflows win ties); use status disabled to deactivate a workflow without losing its keywords. There's no priority column today — if you need stricter precedence, narrow the keyword sets so two workflows can't both match the same turn.

Quick recipe — wire a fresh workflow to an agent

  1. Go to /app/workflowsNew workflow.
  2. Give it a name. Pick the agent from Scoped to agent (or leave on Workspace-wide).
  3. Status: pick Active if you're ready to ship it immediately, or Draft while you're still editing.
  4. Add at least one keyword in the trigger config (e.g. refund) and pick a match mode.
  5. Add at least one step — a message bubble works.
  6. Save.
  7. On the targeted agent's public widget, type a message containing the keyword. The workflow runs instead of the LLM, and you'll see the scripted reply.

To stop a workflow without deleting it, flip its status to disabled. To re-target it from one agent to another, just change Scoped to agent on the same workflow row — no need to clone the steps.

Capabilities

  • One trigger type: on keyword, with three match modes (any / all / exact).
  • Six step types: message, question, branch, tag_lead, webhook, escalate.
  • Variable interpolation: {{var_name}} in any message text resolves the captured value at runtime.
  • Conditional branching on captured answers (5 match operators + a default fallback).
  • Side-effect steps for tagging leads and POSTing to external webhooks (queued, fire-and-forget).

Step types

TypeWhat it does
message Send one chat bubble. Supports {{var_name}} interpolation. Walks straight to the next step.
question Send one bubble + pause the run. The visitor's next message is captured into var_name and the run resumes from the next step on the following turn.
branch Evaluate vars[var] against an ordered list of cases and jump to the first matching go_to step index. The case operators are equals, contains, starts_with, is_empty, not_empty, and default (a fallback fired when no other case hits). Loop-guarded at 32 jumps per turn so a malformed graph can't hang the engine.
tag_lead Append string tags to the conversation's Lead row (creates a stub Lead if the visitor hasn't submitted the inline form yet). Tags accumulate and dedupe on fields.tags. Typical use: tag a visitor as pricing_intent after a branch lands them on the Pro lane.
webhook Fire-and-forget POST (or GET) to an external URL with the run's vars, conversation_id, workflow_id, and any extra_payload. Dispatched as a queued DispatchWebhookJob so the visitor's chat surface never waits on a slow integration. Failures land in /admin/jobs/failed after 3 retries.
escalate Send a goodbye bubble + flag the run as escalated. The conversation is now in the inbox for an operator to claim from the takeover UI.

Trigger match modes

ModeMatches when
any (default) At least one keyword appears as a case-insensitive substring of the visitor's message. "pricing" matches "tell me about pricing please".
all Every keyword must appear as a substring. Useful for compound qualifiers — ["enterprise", "security"] won't fire on "tell me about pricing", but will on "enterprise security review".
exact The trimmed lower-cased message equals one of the keywords verbatim. Use for single-word commands ("cancel", "help", "support") that need to NOT fire on substring matches.

Branch routing

A branch step's cases array is walked in order. The first case whose match evaluates true wins, and execution jumps to go_to (a step index). match: default is a special case that always matches and should sit last — it's the catch-all when none of the preceding operators fire.

{
  "type": "branch",
  "var": "plan_interest",
  "cases": [
    { "match": "equals",      "value": "free", "go_to": 5 },
    { "match": "contains",    "value": "pro",  "go_to": 8 },
    { "match": "not_empty",                    "go_to": 12 },
    { "match": "default",                      "go_to": 15 }
  ]
}

A flow can chain branches — the loop-guard caps execution at 32 jumps per turn so two branches accidentally pointing at each other can't infinite-loop. When the guard trips the run is marked failed and whatever bubbles were emitted before the loop are still flushed to the visitor.

Trigger semantics

The runtime walks every active workflow whose workspace matches the conversation's agent and whose agent_id is either NULL (workspace-wide) or matches the conversation's agent. Within those rows, the first workflow whose keywords match (per its match_mode) wins. Order on the index page is by updated_at descending — most-recently-saved workflows are tried first.

Editing — visual canvas (default) and linear form (alt)

Two ways to edit a workflow, both backed by the same JSON shape:

  • Visual canvas at /app/workflows/{id}/canvas — the default editor. Clicking a workflow on the index page or saving a freshly-created flow lands you here. React-Flow editor with one node per step, draggable connections between handles, and a end-side inspector panel. Branch nodes have one handle per case (plus an optional default handle); wire each handle to the next step.
  • Linear form at /app/workflows/{id}/edit — the alternate. A stack of step cards with type-specific fields. Reach it from the canvas page's "Linear edit" button when a flow is simple enough that a flat list reads cleaner than a graph.

The canvas persists the visual layout (node positions + edge connections) under definition.canvas, alongside the runtime-consumed definition.steps array — so flows authored in the canvas re-open with the same arrangement next time.

Saving from the canvas runs a topological walk of the graph from the trigger node and serialises the steps array in BFS order. Branch cases[*].go_to are populated from the edge target indices, so re-saving a flow you only opened to read leaves the steps unchanged.

Status

  • Draft — never matched. Use this while editing.
  • Active — matched on every visitor turn.
  • Disabled — kept for history but never matched. Useful for seasonal flows you'll re-enable later.

Phase 3 outlook

The persistence shape stays forwards-compatible. Phase 3 will likely add:

  • A/B testing — multiple workflow variants share a trigger; the runtime picks one per visitor and records which one converted.
  • Per-flow analytics — drop-off rates per step, branch-arm distributions, time-to-handoff.
  • Visitor-segment triggers (in addition to keywords) — page URL pattern, returning vs new, GeoIP.

Older runtimes reading a Phase-3 definition will silently skip step types they don't recognise, so a future admin can save a definition that gracefully degrades on an older deployment.