# Act on a request's outcome in n8n

Start a workflow when a request is completed, expires or is canceled, or pause the workflow that sent it until the recipient answers, and branch on the verdict.

## Act on a request's outcome in n8n

n8n can hear about a request's outcome in two ways: a formbase Trigger that runs for every request to a form, or a Wait node that pauses the workflow that created the request. Both end in a Switch on approve, decline or changes.

<p>
  You need a <a href="/guides/n8n/connect">formbase credential in n8n</a>, and your n8n must be reachable from the internet over HTTPS; see{' '}
  <a href="/guides/n8n/install-the-node">Install the formbase node in n8n</a>. To branch on a verdict, the form needs a{' '}
  <a href="/requests/decisions-and-approvals">Decision question</a>. This guide uses an Approval form with one.
</p>

<h2 id="trigger">Start a workflow from the trigger</h2>

<h3 id="pick-the-event">1. Pick the event</h3>

<p>
  Add a <strong>formbase Trigger</strong>, pick the credential and the form, and choose one of the request events under{' '}
  <strong>Event</strong>:
</p>

<h3 id="test-the-trigger">2. Test it with a real request</h3>

<p>
  A test request never starts a request trigger, so send a real one with Delivery set to None, as in{' '}
  <a href="/guides/n8n/send-a-request">Send a request from an n8n workflow</a>, and open its link yourself. Then click{' '}
  <strong>Execute step</strong> and submit the form within two minutes.
</p>

<p>
  The event carries a <strong>data.request</strong> object next to the answers. Its <code>outcome</code> is <code>approve</code>,{' '}
  <code>decline</code> or <code>changes</code>, and its <code>externalId</code> is the ID you set, so you can find the record the request
  was about. Every field is in the <a href="/requests/callbacks#payload">callback payload</a>.
</p>

<p>
  Expired and canceled events are hard to catch with <strong>Execute step</strong>. Publish the workflow and check{' '}
  <strong>Executions</strong> instead.
</p>

<h3 id="branch-on-the-outcome">3. Branch on the outcome</h3>

<p>
  Add a <strong>Switch</strong> node with one routing rule per verdict. Each rule compares <code>{'{{ $json.data.request.outcome }}'}</code>{' '}
  with a value; turn on <strong>Rename Output</strong> to name the branch after it:
</p>

<p>
  Branch on <code>data.request.outcome</code> rather than <code>data.answers.decision</code>: it is missing, not some other value, when the
  request expired or was canceled. Compare values, never the labels in <code>data.display</code>: a label changes when you rename the option
  or the recipient answers in another language.
</p>

<p>
  Connect a node to each branch, then <strong>Publish</strong> the workflow. From then on every completed request to the form runs it.
</p>

<h2 id="wait-for-the-outcome">Wait for the outcome in the same workflow</h2>

<p>
  Here the workflow that sends the request pauses until the recipient answers, then carries on. It takes three changes to the workflow from{' '}
  <a href="/guides/n8n/send-a-request">Send a request from an n8n workflow</a>.
</p>

<h3 id="turn-on-wait">1. Turn on Wait for the Outcome</h3>

<p>
  In the <strong>Create request</strong> node, turn on <strong>Wait for the Outcome</strong>. The node sends formbase this execution's
  resume URL as the request's callback. Leave <strong>Callback URL</strong> empty; the node refuses both at once.
</p>

<p>
  Each execution has its own resume URL, so an External ID reused from an earlier execution fails. Include{' '}
  <code>{'{{ $execution.id }}'}</code> in it, or leave it empty.
</p>

<h3 id="add-a-wait-node">2. Add a Wait node</h3>

<p>
  Add a <strong>Wait</strong> node right after it, with <strong>Resume</strong> set to <strong>On Webhook Call</strong> and the HTTP method
  left at <code>POST</code>.
</p>

<h3 id="branch-on-the-event">3. Branch on the event</h3>

<p>
  Add a <strong>Switch</strong> after the Wait node. The Wait node puts the event under <code>body</code>, so compare{' '}
  <code>{'{{ $json.body.data.request.outcome }}'}</code>. To tell a completed request from an expired or canceled one, compare{' '}
  <code>{'{{ $json.body.type }}'}</code> with <code>request.completed</code>, <code>request.expired</code> or <code>request.canceled</code>.
</p>

<p>Run the workflow. The execution stops at the Wait node until the recipient submits, then resumes down the matching branch:</p>

<ul>
  <li>
    A run from the editor waits for a real answer too. Test Mode works here: the callback of a test request still resumes the workflow.
  </li>
  <li>
    A request expires after 30 days unless you set <strong>Expires At</strong>, and expiry resumes the execution with{' '}
    <code>request.expired</code>. Set <strong>Expires At</strong> to end the wait sooner.
  </li>
  <li>
    The Wait node does not check the <code>X-formbase-Signature</code> header; it relies on the resume URL being hard to guess. If that is
    not enough, use the formbase Trigger, which checks every event.
  </li>
</ul>

<h2 id="troubleshooting">If nothing runs</h2>

<ul>
  <li>
    <strong>It was a test request.</strong> A request created with Test Mode on, or with <strong>Try it yourself</strong>, never starts a
    request trigger. Only its <a href="/requests/callbacks">callback</a> fires.
  </li>
  <li>
    <strong>The request was for another form.</strong> The trigger listens to the form you chose in it.
  </li>
  <li>
    <strong>The workflow is not published, or its credential reaches another workspace.</strong> See the checks in{' '}
    <a href="/guides/n8n/public-link-submissions#troubleshooting">the public link guide</a>.
  </li>
  <li>
    <strong>
      Create request fails with <em>callbackUrl is not allowed</em>.
    </strong>{' '}
    Your n8n hands out an <code>http://</code> or private resume URL. See{' '}
    <a href="/guides/n8n/install-the-node#public-address">Tell n8n its public address</a>.
  </li>
  <li>
    <strong>The Wait node never resumes.</strong> formbase cannot reach the resume URL, for example because the tunnel is down or the proxy
    blocks <code>/webhook-waiting/</code>.
  </li>
</ul>

<h2 id="next">Next</h2>

<div class="not-prose grid gap-3 sm:grid-cols-2 mb-8">
  - [Look up, remind and cancel requests in n8n](/guides/n8n/manage-requests) — Find a request by External ID, nudge the recipient, or withdraw it.
  - [Decisions and approvals](/requests/decisions-and-approvals) — The Decision question and its three values.
</div>
