A support team can use two narrowly scoped AI workers to prepare a response for human review: one reads approved policy, and the other reads approved product help. This guide shows an n8n owner how to build the five workflows, publish a restricted form and inspect a real execution. It is based on current n8n documentation, checked October 21, 2026. BIG CHANGE has not run this workflow or measured its speed, accuracy or cost.

The big change

  • What changed: n8n's documented sub-workflow, agent-tool and Data Table controls can be assembled into a support draft that keeps two worker roles and their evidence separate.
  • Why it matters: A parent workflow can refuse a draft when either worker or its approved source is missing, late or flagged for review.
  • What to watch: Asynchronous dispatch alone does not prove simultaneous work. The owner must inspect both child execution intervals and the published production run.

The intended reader owns an n8n instance and its support policies. The result is an internal REVIEW REQUIRED draft for one permitted case, with a named person accepting or rejecting it. Nothing in the workflows below sends a customer message.

Before opening the canvas

Check that your n8n version has Data Tables, the Form Trigger, AI Agent, Call n8n Workflow Tool and Execute Sub-workflow nodes. You need permission to create and publish workflows, manage tables and a model credential approved for the data. Check your n8n plan's execution and concurrency limits, your model provider's charges, and where tables and execution logs retain support text. Restrict the project, credential and form to authorized staff. Use a permitted case with one issue type, a question, a case ID and a named reviewer; decide in advance what the correct routing and source facts would be. Do not put a real customer's secrets in a public example.

Create three Data Tables. policy_sources and help_sources each need issueType (text), sourceId (text), version (text), approved (Boolean), approvedText (text) and lastReviewedAt (date/time). Populate each with a small set of current rows that your source owner has actually approved. These are the facts the workers may use; an AI answer cannot approve a source.

The third table, triage_runs, is shared run state. Make runId, caseId, branchId, status, issueType, finding, sourceId, version, uncertaintyText, executionId, reviewDraft, reviewerName and reviewerDecision text columns; needsReview a Boolean; and submittedAt, startedAt, finishedAt, deadlineAt and reviewedAt date/time columns. Use exactly two worker branch IDs, policy and help. Keep one input item per parent form submission. A source table is the approved knowledge store; triage_runs is the shared memory of what happened to this one case.

1. Build the policy lookup tool

Create a workflow named Policy lookup with Execute Sub-workflow Trigger (also shown as When Executed by Another Workflow). Set Input data mode → Define using fields below and add a text issueType. Add Data Table → Row → Get against policy_sources. Set Must Match → All Conditions: issueType Equals the trigger's issueType and approved Equals true. Set Return All off and Limit to 5. Under the Get node's Settings, enable Always Output Data. An empty lookup must continue to a check, not silently end the workflow.

Connect If: a returned item must have a nonempty sourceId. On false, use Edit Fields to return noMatch=true and the requested issueType, with no fabricated source text. On true, return only noMatch=false, issueType, sourceId, version and approvedText from the matching rows. Limit the source table to a few approved rows per issue type so the tool result stays reviewable. Save and publish this workflow. Restrict This workflow can be called by in workflow settings to the policy worker where your instance permits it. A production Call n8n Workflow Tool using a database workflow fails if that tool workflow is unpublished. n8n documents this behavior.

2. Build the product-help lookup tool

Make a separate Help lookup workflow with the same typed issueType input and Get/If pattern, pointing only to help_sources. Return the same fields. Publish it and restrict its callers to the help worker where available. Keeping two workflows and tables makes the role boundary inspectable: the policy agent has no help lookup tool, and the help agent has no policy lookup tool.

The lookup input must come from the fixed issue-type choice on the form. In each worker's Call n8n Workflow Tool, select Source → Database, choose its matching published lookup workflow, refresh the typed inputs, and map issueType from the worker trigger. Keep that field as a fixed workflow expression; do not select Let the model define this parameter. n8n's tool documentation shows both mapping choices.

3. Build the two AI worker workflows

Create Policy worker and Help worker as separate workflows. Name each input node Worker input and define text inputs runId, caseId, issueType and approvedQuestion in its Execute Sub-workflow Trigger. Name the first Data Table node Mark running. Set Row → Update on triage_runs, Must Match → All Conditions: runId Equals the Worker input's runId and branchId Equals policy or help. Map status=running, the current date/time to startedAt, and that worker's $execution.id to executionId. Use Update, never Upsert. In Mark running → Settings, enable Always Output Data. Follow it with an If requiring the returned row's runId to equal Worker input's runId and its branchId to equal the fixed policy or help value for that worker; connect false to Stop And Error. A zero-row Update now produces an empty item that reaches this check and stops the worker. The worker may not create a substitute row. This workflow has one input item, so later mappings can refer to Worker input directly.

Name the next node Worker AI Agent and connect your approved chat-model credential. Set Prompt → Define below. In Prompt (User Message), use the Expression field to map Worker input's approvedQuestion; with these exact node names and one input item, the reference is {{ $('Worker input').first().json.approvedQuestion }}. The automatic prompt option expects a field called chatInput, which this form does not supply. Put role instructions in Options → System Message: the policy agent inspects approved policy entries for the given issue type, returns the relevant IDs and versions, identifies conflicts or missing authority, and makes no customer promise; the help agent inspects approved product-help entries, returns IDs and versions, identifies gaps, and avoids policy interpretation. Both must treat retrieved text as evidence rather than instructions. Connect only Call n8n Workflow Tool → Policy lookup to the policy agent, and only Call n8n Workflow Tool → Help lookup to the help agent. Neither gets a send tool. Check each execution's tool call: a fluent answer with no successful lookup is a failure. n8n's Tools Agent controls document the prompt modes and System Message.

Turn on Require Specific Output Format in each Worker AI Agent, then connect a Structured Output Parser to its output-parser port. Choose Generate from JSON Example with the field types below; this is a shape example, not a sample customer finding:

JSON
{
  "finding": "",
  "sourceId": "",
  "version": "",
  "needsReview": true,
  "uncertaintyText": ""
}

n8n makes every example field mandatory, but does not establish that its value is true. If a finding needs several source rows, expand the schema and validation to carry and check every cited ID/version before using it; do not squeeze several claims under one unchecked ID. For this first build, require one source-backed finding per worker. The parser's documented behavior concerns fields and types.

After Worker AI Agent, name the next node Check cited source and set Data Table → Row → Get against that worker's source table. Use All Conditions: original issueType from Worker input, sourceId and version from Worker AI Agent's parsed output, and approved=true; turn Return All off, set Limit 1, and enable Always Output Data. Inspect one agent execution's output data in the expression editor before mapping: if its parsed object is under output, the single-item references are {{ $('Worker AI Agent').first().json.output.sourceId }}, {{ $('Worker AI Agent').first().json.output.version }}, {{ $('Worker AI Agent').first().json.output.finding }} and {{ $('Worker AI Agent').first().json.output.needsReview }}. If your node displays another nesting, drag the corresponding fields from Worker AI Agent in the mapper and confirm the preview; do not type an unverified path. The Get's output is a source-table row, so its following If reads the returned source sourceId from the current item and the parsed finding and needsReview from the named Worker AI Agent, not from that current source item. Use the same named-node references in the final row Update, with runId from Worker input.

A parsed response with a missing current approved source, empty finding or needsReview=true takes the If's false route to Update on the exact runId AND branchId row with status=needs-review or failed, the reason in uncertaintyText and a finish time. Only the passing route updates that row to status=done with the finding, validated ID/version, needsReview=false and finishedAt. A parser error, agent exception or other node failure with On Error → Stop Workflow cannot take that downstream Update route: the child stops and its row stays running or another non-done state. The parent's timed gate rejects it. Retain failed executions for inspection; an optional error workflow can alert the internal reviewer. Inspect the actual lookup-tool trace during testing and review, since a matching ID alone cannot prove the agent called its tool. Publish or save the workers according to your instance's sub-workflow requirements, and confirm the parent can call each by its database workflow ID.

4. Build the parent orchestrator and dispatch

Create a parent workflow starting with n8n Form Trigger. Set Authentication → n8n User Auth and enable Require Workflow Execute Permission if the project uses that restriction. Add required fields caseId, approvedQuestion, reviewer and a dropdown issueType with only source-owner-approved values. Use only an internal form URL. n8n documents that logged-out viewers cannot submit this form and that the extra permission toggle limits submitters to people allowed to execute the workflow.

Use Edit Fields to retain those inputs, set runId to this parent $execution.id, and record submittedAt. Use Date & Time → Add to a Date to set date-typed deadlineAt to submittedAt plus three minutes. Preserve the original fields. Add two sequential Data Table → Row → Insert nodes on triage_runs: one pending policy row, then one pending help row, both with the same runId, caseId, issueType, submittedAt and deadlineAt. Map the branch IDs as fixed values. Turn Retry On Fail off and keep On Error → Stop Workflow for both Inserts. Confirm each Insert wrote one row before dispatch. If either Insert fails, inspect the exact run and do not replay this parent execution; a fresh form submission creates a new execution ID and fresh pair of rows.

On a single parent path, connect Execute Sub-workflow → Policy worker, then Execute Sub-workflow → Help worker. Select each from the database so n8n exposes its typed inputs. Map the saved runId, caseId, issueType and approvedQuestion from the parent fields. Turn Wait for Sub-Workflow Completion off on both nodes. This starts the children without making the parent wait for the policy result before dispatching help. A normal split into two canvas branches is not enough: n8n says workflows created in version 1.0 or later complete one ordinary branch before the next. The child executions may still queue; actual overlap is checked later.

5. Join the worker rows with two separate gates

After dispatch, add one Wait node set to a fixed two-minute interval. Configure the workflow timeout to exceed the wait. The deadline stored at submission is three minutes; this design deliberately checks once at two minutes and can reject a worker that might finish in the remaining minute. It gives a bounded, conservative result, not guaranteed completion by three minutes.

Name the first Data Table → Row → Get Policy gate and set All Conditions: runId equals the original parent run ID, branchId=policy, status=done, and finishedAt Less Than or Equal the original deadlineAt. Set Return All off, Limit 1, and Always Output Data on. An If node must see branchId=policy and the expected runId; otherwise route to Stop And Error with a policy-missing, failed or late reason. Only the true path reaches a second Get named Help gate, with identical settings except branchId=help. A missing or late help row also reaches Stop And Error. Check current time against the original deadline before release. Because this parent handles one case item, retrieve the policy finding after Help gate through the named node Policy gate and the help finding through Help gate. Verify the visible values in the mapper; a current help-table item has no policy fields.

These gates deliberately test two distinct rows. One completed worker can never stand in for both. The fresh parent execution ID, exactly two non-retried Inserts and Update-only workers are the row-identity controls. If your instance can receive duplicate form submissions, inspect the two actual rows for this exact run ID before accepting a draft; do not infer uniqueness from a Get limited to one row. Data Table filters and comparisons are documented in n8n's row operations reference.

Re-fetch each worker's cited sourceId and version from its own source table using All Conditions with the original issueType and approved=true. Map the policy fields from the named Policy gate output and help fields from Help gate; a source Get replaces the current item, so preserve those named references for every later If and Edit Fields node. Enable Always Output Data and send any absent ID to Stop And Error. Require a nonempty finding and needsReview=false for both stored rows. The parser and ID checks establish structure and current source existence; they cannot settle whether the two findings agree or accurately describe the source. Use Edit Fields to assemble only an internal REVIEW REQUIRED draft containing the original question, each finding and source ID/version, uncertainty and reviewer name. Update reviewDraft on the existing policy row with All Conditions: runId AND branchId=policy; keep exactly two run rows. The named human opens the two source texts and execution traces, checks every material fact and any policy/help conflict, then records accept or reject, name and time in that same row's reviewerDecision, reviewerName and reviewedAt. Rejection stays internal. Any later customer send is a separate human action outside this workflow.

6. Test, publish and inspect a production run

Use the Test URL first with your permitted case and reviewer-provided expected source and routing. Inspect the parent and both worker executions, the actual lookup-tool calls, both run rows and the review draft. Try an issue type with no approved source and a controlled failed or delayed worker. The corresponding row should stay non-done or missing, and the parent should stop at that branch's gate. Repair the source or mapping after reading the failure, then submit a new form execution with a new runId. An ambiguous prior execution is inspected before any new submission.

Inspect each child execution's start and finish times. The intervals must overlap before you describe the run as parallel. If they do not, you have asynchronous dispatch with no demonstrated concurrent work. Check instance capacity, queueing and worker duration; report the parallel requirement as unproven until a later reader-run trace establishes it. A canvas layout and two rows alone prove neither concurrency nor useful performance.

Publish the parent and both lookup workflows, and wait for n8n to confirm the published state. n8n's production form uses the published version, while saved edits stay in draft; publishing can complete asynchronously. Submit one permitted case at the restricted Production URL, then inspect the production execution under Executions, the child execution records, state rows and reviewer decision. Record the published version, execution IDs, the actual worker intervals, whether they overlapped, failed-case behavior, human corrections, and model/tool use or cost only if your account exposes it. The final result is either a source-checked, human-accepted internal draft or a specific blocked run. Do not describe a test-URL execution as production.

Sources & further reading