Subchapter 27.9
references/post-build-flow.mdMarkdown33 KBView on GitHub
Use this skill after workflow_builder_build_workflow succeeds on a direct orchestrator build,
especially when the build result contains postBuildFlow.required: true, or when
the current message contains <workflow-verification-follow-up> or
<workflow-setup-required>.
One-off builds (postBuildFlow.reason: "direct-one-off-build-succeeded") hand
off to the one-off-operations skill instead — verification is optional there
and completion is a live run with read-back. If both sets of instructions are
in context for a one-off build, the one-off flow wins.
These instructions are in English, but user-visible text you write while following them stays in the user’s conversation language.
For trigger inputData shapes, read the workspace file
knowledge-base/reference/trigger-input-data-shapes.md when a sandbox
workspace is available, or load the trigger-input-data-shapes reference.
Use this section when the system prompt describes the persistent setup panel,
setup returns announced: true, or the current user input contains
<workflow-test-request>. Otherwise, keep the setup card flow below.
announced: true, summarize the open items and any
validation warnings. End the turn. The user can complete setup in the panel
while chat stays available. Do not wait, poll, or open a trigger-test card.<workflow-setup-state> over earlier setup
results. If items settled, none remain open, and there are no validation
warnings, verify the current saved configuration with workflow_builder_verify_built_workflow.
It refreshes the credential plan.
Report remaining simulations or connection failures. Do not claim live
success from the earlier build result.<workflow-test-request> in the current user input means the user clicked
Execute. A block in conversation history does not request another execution.
Use the workflow ID in the current block. Inspect its current
<workflow-setup-state> and read the saved workflow with
get_workflow_as_code. Do not call setup_workflow
for this precheck. It announces setup and ends the turn. If the target is
absent from the state block, inspect its saved configuration. If required
setup cannot be confirmed, report what is missing and end the turn.
If required items remain open for this
workflow, report them and end the turn without a live run. Otherwise,
use run_workflow with suitable trigger input. The user has
already requested this test; do not ask whether they want it. The execution
tool still enforces its approval policy. Do not publish the workflow to test it.debug_execution. Fix the same workflow when
possible. Use the current saved source so panel edits are preserved. Report
unresolved setup or failures in chat. Before another live run, inspect the
successful effect nodes from the failed run. Follow
Cleaning up after a live test for any artifacts
they left behind. After a repair and that artifact check, test the updated
workflow and inspect its output. Do not substitute mocked verification
for the requested execution.A setup card that was already open keeps its apply and trigger-test resume
flow. Its result is not a panel announcement unless it has announced: true.
When the current message contains <workflow-verification-follow-up>, verify
immediately from the payload’s obligation — do not acknowledge first. If the
obligation is ready_to_verify or verifying, call workflow_builder_verify_built_workflow. Do
not call setup_workflow in this turn and do not declare the
workflow finished if outcome.setupRequirement.status === "required" — setup is
routed automatically as a separate <workflow-setup-required> step after
verification. For a multi-trigger outcome, verify every trigger that does not
yet have a recorded successful verification. Make all of these calls in this turn.
When the current message contains <workflow-setup-required>, your first action
is to call setup_workflow with the workflowId from the payload. Do
not verify, do not ask, do not write a message first — the inline setup card in
the n8n Assistant panel is the user-visible surface. If the result has
announced: true, use the persistent panel instructions above and end the turn.
If it returns deferred: true, respect the user’s choice and do not retry with any other setup tool.
A result carrying skippedByUser names credentials the user already passed on:
never re-open setup for those, in this turn or any later one — see
Credentials the user skipped.
After setup completes or is applied, follow
Mocked verification live-test follow-up
if the payload or prior verification evidence says mocked credentials,
simulated node output, fixture overrides, temporary pin data, or another mocked
input was used.
Pick in this order:
slackApi, notionApi, …) whenever one
exists — search with search_credential_types.httpTemplatedCustomAuth) for any service
without a dedicated type whose auth is expressible as header/query/body
values — which covers API keys and bearer tokens (Authorization: Bearer <token> becomes {"headers":{"Authorization":"Bearer {{api_key}}"}}, not
httpBearerAuth). Always provide a recipe (below) so the user only pastes
their secret.httpBasicAuth, httpDigestAuth, oAuth2Api, …)
only for what a template cannot express: basic auth’s base64-encoded pair,
digest’s challenge-response, OAuth flows — or when the user explicitly asks
for a specific plain type: an explicit user choice wins (setup accepts it
with allowPlainGenericAuth: true).When the workflow authenticates a service through Simplified Custom Auth
(httpTemplatedCustomAuth), include credentialHints in the same
setup_workflow call, so the setup card pre-fills
the credential and the user only pastes their secret. Before you compose the
hints, load the credential-recipe-research reference and follow it. It lists
every field and its rules.
Skipping is remembered for the whole conversation. A setup result may carry
skippedByUser (nodes and credential types the user passed on), and a build
outcome may carry setupRequirement.status === "not_required" with
reason: "skipped-by-user". In both cases the blocking setup card is off the
table for those credentials — including after later edits, rebuilds, and
<workflow-setup-required> steps. Asking again is the single most common
complaint about this flow.
Instead, in your normal message:
Only once the user asks for a specific credential — “connect Slack now”, “let’s
do the Slack setup”, or picking it out of an offer you made — call
setup_workflow(reopenSkipped: ["slackApi"]), naming just what they
asked for so the rest stays skipped. A generic “yes” to an unrelated question is
not an ask.
Pass the reopenWith value the tool reported for that card, not the user’s
wording — a credential type for a credential card, a node name for one that was
only missing a parameter. If nothing matches, setup answers with
unknown_reopen_target and the list you can choose from; pick from it or tell
the user what they named isn’t part of this workflow. Don’t fall back to
re-offering, the user already asked.
Publishing is never required for testing. Both run_workflow and
workflow_builder_verify_built_workflow inject inputData as the trigger’s output — the
workflow does not need to be active. Form, webhook, chat, and other event-based
triggers are all testable while the workflow is unpublished. Never publish a
workflow as a precondition for running it.
Webhook input must carry the fields the workflow reads. A flat inputData
becomes the request body only; query, headers and params stay empty. When
any expression reads $json.query.*, $json.headers.* or $json.params.*, pass
the request envelope { body: {...}, query: {...}, headers: {...}, params: {...} } (or a
fixtureOverrides entry on the trigger node). Otherwise the field resolves empty,
the run still succeeds, and that field is unverified — say so instead of
reporting it as working.
Do not proactively offer, recommend, or mention publishing until a successful
execution has run every required node on the claimed path without mocked
credentials, simulated node output, fixture overrides, or temporary pin data
for those nodes. Before you publish, offer publishing, count a run the user
started as evidence, or repair a workflow that is already published, load the
publishing reference and follow it.
For post-build verification of workflows produced by workflow_builder_build_workflow, always verify with
workflow_builder_verify_built_workflow, never with raw run_workflow. It reuses
the build outcome simulation plan, mocked credentials, and temporary pin data, so
destructive nodes are pinned and it is safe to call repeatedly. A raw
run_workflow runs the workflow live with no pin data, and on a
workflow you just verified it surfaces a redundant run-approval prompt to the
user right after verification already executed the workflow. For follow-up
requests like “verify again”, call workflow_builder_verify_built_workflow with workflowId even
if the original workItemId is not in context. For alternate deterministic
scenarios, pass fixtureOverrides keyed by simulated node name instead of trying
to force data through the trigger.
run_workflow_step is a debugging tool, not a verification
tool. It runs one node and tells you what that node returns. It says nothing
about the rest of the chain, so it never settles a verification obligation and
never turns “partial coverage” into “verified”. Use it to inspect one node —
most often with reuseExecutionId on a node that failed a real run — and keep
verifying with workflow_builder_verify_built_workflow. A step run with mockInput proves even
less: the result names the nodes whose output it invented in
mockedNodeNames, and you must repeat that limitation in what you tell the
user.
Reserve run_workflow for runs the user explicitly asked for
(e.g. “run it now”, “execute it against my real data”). Never call it on your own
to re-test, expand coverage, or “prove the full chain” of a workflow you just
built or verified: re-run workflow_builder_verify_built_workflow instead — with
triggerNodeName to reach another trigger’s branch, or fixtureOverrides to
reach another branch within one trigger’s run — or report the partial coverage
and let the user decide whether to run it.
If fixtureOverrides is rejected with invalid_fixture_override, the target
node was not classified as simulated in the build outcome. Do not retry the same
override. If that node’s data controls a branch that needs verification and you
have the source file, load workflow-builder, declare representative output
fixtures on the controlling upstream node, rebuild the same workflow, and verify
again.
Never edit or copy a saved workflow to reach a branch. Disabling, deleting, or reordering nodes to steer a test mutates the user’s workflow and leaves it broken for as long as the test runs — if it is published, its triggers fire against the broken version. Building a throwaway second workflow is no better: the evidence is gathered against a copy that can drift from the workflow the user keeps, and the copy is left behind whenever the cleanup delete fails.
For a workflow with more than one trigger (triggerNodes has multiple entries),
verify once per trigger:
triggerNodeName to workflow_builder_verify_built_workflow and call it once for each
entry in triggerNodes. Naming no trigger verifies only the auto-detected
one. An unresolvable name is rejected outright, so a rejected call means the
name is wrong — re-read triggerNodes, never fall back to editing.nodesNotReached only for its selected trigger’s main-flow
branch. Coverage is the union across successful passes. Run every trigger
before you report a workflow coverage gap. Different triggers can select
different outputs of a shared Switch or If node.claim to
describe the result (see “Claiming success”).triggerNodeName to
run_workflow the same way — one run per trigger — and report
each branch’s result.workflowId, workItemId, triggerNodes, verificationReadiness,
setupRequirement, and postBuildFlow from the tool output. If the output
is missing a workflowId, explain that the build did not submit.
get_workflow_as_code(workflowId) or read the bound
workspace source file, and compare the actual graph to the user’s requested
outcome. Build/save success only means a workflow was saved; it does not
prove the saved workflow is good.workflow-builder skill and patch the same workflow with workflow_builder_build_workflow
using the existing workflowId and workItemId; then inspect and verify
again.verificationReadiness.status === "already_verified", do not repeat
automatic verification. Read the saved claim before describing the workflow
as verified. For tracked multi-trigger builds, follow the verification
obligation until every trigger has a successful pass.verificationReadiness.status === "ready", call
workflow_builder_verify_built_workflow with the workflowId, the workItemId when you
have it, and the trigger-appropriate inputData shape. When triggerNodes
has more than one entry, call it once per trigger with triggerNodeName.verificationReadiness.status === "needs_setup" and the persistent setup
panel is enabled, first try workflow_builder_verify_built_workflow when verification has not
run. It can verify simulated paths or report that a simulation plan is
unavailable. Then announce setup with setup_workflow. Do not use
a live execution to work around a blocker.verificationReadiness.status === "needs_setup" and the persistent setup
panel is disabled, call setup_workflow with the workflow ID.
The user configures the workflow through the inline setup card.verificationReadiness.status === "not_verifiable", do not infer
lower-level verification conditions; use the readiness guidance to give a
clear warning or manual-test note. This is a warning completion state, not
a verified state and not an infinite blocker.workflow_builder_verify_built_workflow result with
success: true but a non-empty nodesNotReached is partial evidence:
the execution ended early (see lastNodeExecuted and coverageNote) and
the listed nodes — including any planned simulations — never ran.
data_tables(action="insert-rows"),
re-run workflow_builder_verify_built_workflow, and delete the test row afterwards. The same
holds for data you seed anywhere else to unblock a run — it is yours to
remove once the run is done (see “Cleaning up after a live test”).run_workflow
yourself to reach those nodes; offer the user a test instead. Never claim
end-to-end verification when nodesNotReached is non-empty — except for
nodes another trigger’s pass already reached, since per-trigger coverage
is the union across passes.output fixtures to that upstream node, rebuild the same workflow, and
re-run workflow_builder_verify_built_workflow with fixtureOverrides. Only fall back to a
manual-test note when you cannot safely patch the source or the repair
budget is exhausted.simulationNote (nodes whose output was simulated) to the user
whenever it is present.resolvedParameterWarnings. A simulated node’s preview is fixture
data: it never proves an expression resolved. Each warning names a
parameter that resolved to empty or threw on the real input — the usual
causes are a trigger input that lacks the field (body-only webhook input
for a $json.query.* expression) or a wrong expression. Fix the input
shape or the expression, re-run, and never report that field as working
while a warning stands. Each warning carries the execution ID that was
checked. Use that ID with get_resolved_node_parameters
to inspect the same input.skippedParameterChecks. These nodes have unchecked dynamic fields.
The list shows at most 20 checks. skippedParameterCheckCount includes
omitted checks, which also leave dynamic fields unverified.
State that limitation even if the run succeeded and no parameter warnings
were returned. Do not request parameter values when sharing is disabled.setupRequirement.status === "required" and
setup has not already run for this build, call setup_workflow
with the workflowId.setup_workflow returns announced: true, summarize the
panel state and end the turn. The live-test follow-up waits for a later user
turn or Execute request. When the tool opens the inline setup card, the card is the
user-visible surface. Do not tell the user to open the editor, use the canvas,
or click a Setup button; the user does not need to navigate anywhere.setup_workflow returns deferred: true, or reports
skippedByUser, or applies only part of the card, respect the user’s
decision — do not retry with setup_credentials, another
setup_workflow call, or any other setup tool. partial: true
with nodesStillNeedingSetup is not permission to re-open the card in the
same turn: report what remains as described in
Credentials the user skipped.credentialResolutionNote says Gateway credits are depleted,
that note wins: do not offer a live test.workflow_builder_verify_built_workflow already
proved it works end-to-end with full coverage.publish_workflow when the user explicitly asks to
publish, and load the publishing reference first.error-workflows and follow its
error workflow follow-up.
Do not replace this explicit opt-in with a generic “add
anything else?”, publish, or test question.After workflow setup completes or is applied, if the latest verification for
that workflow used mocked credentials, simulated node output, fixture overrides,
temporary pin data, or another mocked input, ask whether the user wants a live
test without mocks. Ask only about the live test. Do not run it automatically.
An explicit test request in the current user input, including
<workflow-test-request>, already answers
this question. Run the requested test through run_workflow.
Do not offer publishing as an alternative or describe the workflow as ready to
use or publish.
If credentialResolutionNote says Gateway credits are depleted, that
note wins over this live-test offer: do not offer a live test. Tell the user
they must top up Gateway credits or add their own key on the node first.
If the user agrees, use the explicit live execution path (run_workflow
for a direct live run) and report the result separately from the earlier mocked
verification. If the live test fails, treat the workflow as unresolved and do
not offer publishing. If the user declines or defers, state what remains
untested, do not claim live end-to-end verification, and do not offer
publishing.
A live run against real credentials leaves real artifacts — a row in their sheet, a message in their channel, a block on their page. Test data you created is your mess, not theirs.
This applies to any live run in the conversation, not only one from this turn. A test record written three turns ago is still on the user’s page now, and the debt is still yours — carry it forward until it is cleared or the user declines. Undertaking to clean up after some future run does not settle a record that already exists, and a run that failed afterwards does not remove what an earlier successful run wrote.
When a live test, or a verification you seeded data for, wrote/sent/changed anything in an external system:
[Test] … to-do at the bottom of
the toggle”). Read it back from the effect node’s output rather than guessing.one-off-operations is for. Never present manual deletion as how this
gets resolved, and never claim you have no way to delete it — “I don’t have a
delete tool for X” is false whenever X has a write API you just used. Noting
that the user could also remove it by hand is fine only alongside your own
offer.If the user declines, note that the item is still there and move on — don’t re-ask.
Not every write is test data. When the live run was the point — a one-off
operation whose whole purpose is the effect (see one-off-operations) — what it
wrote is the deliverable. There you offer to clean up the workflow, never the
result.
For tracked multi-trigger builds, claim combines the saved successful passes.
A verified claim requires every trigger to pass and real coverage for every planned node.
Verify claim.pendingTriggers within the attempt limit. nodesNotReached
outside the claim describes only the current trigger’s branch.
workflow_builder_verify_built_workflow returns a claim, and its level decides what you may
say:
verified — you may call the workflow verified, tested, or working.partial, unproven, or failed — you may NOT. Name what is unconfirmed
instead.claim.liveState decides separately whether you may call the workflow live. A
run always executes the draft, so verified says nothing about production:
live-stale — the published version is older than the draft you verified.
Do NOT call the workflow live, running, or working in production. Say the fix
is in the draft, and follow the publishing reference.live-current — the published version is the one you verified.unpublished — the workflow does not run in production at all.success: true does not mean verified. It means the run ended without an
error, and a run with every write simulated also ends without an error. Read
claim.level, not success.
Nothing else tells the user how strong the claim is. Your message is the only place they learn it, so name the unreached and simulated nodes yourself, and say what stays unconfigured and what that means when the workflow runs.
Without a claim, do not tell the user a workflow is “fixed”, “verified”,
“tested”, “working”, or has “no errors” unless you have a passing
run_workflow or an inspected user-run execution that exercised the
path being claimed. Do not call a workflow “ready to use” or “ready to publish”
unless a passing execution met the publish-readiness requirement above. A
successful workflow_builder_build_workflow/save, a static validate_workflow, or
your own narration are NOT execution evidence. For a produced artifact (a file,
generated document, or Code-node output), read the real output before calling it
complete; do not infer correctness from the fact that a node ran. The same
applies to rows or records written to an external system: never make quantitative
claims (“22 rows written”, “columns matched”) that you did not read back from
the effect node’s actual output (get_execution_node_output) or from
the target system itself — a successful run status does not prove the right
data was written, only that nodes ran. Output of a simulated or pinned node is
fixture data: never quote it as what the workflow produced, and never cite it as
proof that an expression resolved — use resolvedParameterWarnings or
get_resolved_node_parameters for that. If you could not run the
failing path or inspect the artifact, say so plainly — “I couldn’t verify X
because Y” — and name what is unconfirmed. An honest “could not verify” beats an
unverified success claim.
After building a workflow that uses a trigger with an HTTP endpoint, share the
full production URL with the user. Use the Webhook base URL and Form base URL
from the <instance-urls> block in the user’s turn. Each trigger type has a distinct
pattern:
{webhookBaseUrl}/{path} (where {path} is the node’s
webhook path parameter).{formBaseUrl}/{path} (or {formBaseUrl}/{webhookId} if
no custom path is set). Form Trigger lives under /form/, NOT /webhook/ —
they are separate URL prefixes. Do NOT use the Webhook base URL for Form
Triggers.public parameter — pick the right guidance for the current value,
do not default to sharing a URL.
public: false (the default): there is NO end-user HTTP URL. Tell the
user to open the workflow in the editor and click the Open chat button
on the workflow canvas — that opens the built-in test chat. Do NOT share a
webhook URL, and do NOT suggest flipping public: true just to enable
testing — the in-editor chat is the intended testing path for private chat
workflows.public: true: the public chat URL is
{webhookBaseUrl}/{webhookId}/chat — share it after the workflow is
published. {webhookId} is the node’s unique webhook ID; read it from the
workflow JSON, never guess. End users can open this URL in a browser.
The /chat suffix is unique to Chat Trigger — do NOT append it to Form
Trigger or Webhook URLs. (Your own testing via run_workflow and
workflow_builder_verify_built_workflow works regardless of public or publish state.)These URLs are for sharing with the user only. Do NOT hardcode them into workflow code or build specs unless the workflow actually needs to send or store its own public endpoint.
These rules apply after the first build, while setup runs. Before the first build, do not ask about credentials: the builder preserves explicit valid credentials and auto-mocks missing or unselected ones. Do not ask whether to build now and set up credentials later; building first and routing setup after verification is the default path.
Trust the build outcome over your own source file. When workflow_builder_build_workflow
returns resolvedCredentialsByNode (or setupRequirement.status === "not_required"), the saved workflow is already connected to those credentials,
even if your source used an unresolved newCredential() call. Route credential
setup only when the build outcome reports mocked credentials or
setupRequirement.status === "required".
Let setup collect the choice when a service has multiple credentials of the
same type. The setup card lists every credential of that type, so the user
picks one there. Do not ask this in chat or with ask_user.
Exception: the user already named the credential in their message — use it
directly.
Honor an explicit “create a new credential” request with
preferNewCredentials (see Credential Rules). Never ask the user to choose among
existing credentials, not even when exactly one exists. Setup opens on
credential creation and still lists the existing credentials, so the user can
change their mind — say so in one short sentence. If they had skipped that card
earlier, pass reopenSkipped alongside it: preferNewCredentials decides what
the card offers, reopenSkipped decides whether the card comes back at all.
Ask which auth type to use when a service supports more than one only for a
standalone credential request: setup_credentials
outside any workflow. That picker is locked to a single
credentialType. So when search_credential_types
returns more than one auth option for the service (e.g. notionApi and
notionOAuth2Api), use ask_user with a single-select before
the setup call. List OAuth2 first and present it as the recommended option.
Exception: the user has clearly indicated an auth type (e.g. “api key”, “oauth”,
“personal token”) — map it to the matching credentialType without asking. In a
workflow build, never ask about the auth type, before or after the build: leave
an ambiguous auth type out of early setup, let the build pick it from the node
definition, and let setup collect the credential.