Handling Approval Timeouts and Rejections

Learn exactly what happens when a Spojit Human approval node is rejected, times out, or is cancelled, why there is no "on reject" branch, and how to design workflows that stay safe when an approval does not go through.

Overview

The Human node pauses a workflow until the right people approve or reject the work in front of them. Most of the time you think about the happy path: someone clicks Approve and the run continues. But approvals have three other outcomes you need to plan for: a Rejected decision, a Timed out approval that nobody acted on in time, and a Cancelled approval that was withdrawn. Each of these stops the workflow rather than sending it down an alternate path, and understanding that behavior up front saves you from designing branches that can never run.

In Spojit, a non-approval is deliberately a hard stop. A rejection and a timeout both halt the run, and there is no second output on the Human node to catch a "no" decision. The mental model to hold is simple: a Human node is a gate, not a fork. Everything you place after the node only runs if every approval slot is satisfied with an approval. This guide explains each outcome, what the node outputs, and the design patterns that let you react to a rejection without relying on a branch that does not exist.

The Four Approval Outcomes

When a Human node finishes, it resolves to exactly one of these outcomes. Approvers act in the Approvals inbox at /approvals, on the dashboard widget, or from the menu badge.

  • Approved - every approval slot was satisfied. The run continues to the nodes connected after the Human node. The node output is { approved: true, approvalId, outcome: "APPROVED" }, so downstream nodes can reference values like {{ approval.approvalId }}.
  • Rejected - an approver declined. The run halts with APPROVAL_NOT_APPROVED. Nothing after the node runs.
  • Timed out - the Timeout (minutes) elapsed before the approval completed. The outcome is TIMEOUT, and Spojit treats a timeout exactly like a rejection: the run halts. A blank timeout means the node waits indefinitely and never times out.
  • Cancelled - the pending approval was withdrawn (for example, the run was stopped or the request was retracted). The run does not continue past the node.

Why a Timeout Counts as a Reject

The Timeout (minutes) field on the Human node sets how long Spojit waits for a decision. Leave it blank and the node waits forever. Set a value and, once that many minutes pass with at least one slot still unsatisfied, the node resolves as Timed out. Because a timeout means "no one approved this in the window you allowed," Spojit treats it the same as a rejection and halts the run. This is the safe default for an approval gate: if the team did not say yes, the protected action downstream does not happen.

Practically, that means you should pick a timeout that matches how the request is delivered. If you turn on Email approvers so the first ten approvers are emailed, give people enough hours to read and respond. If approvers only watch the Approvals inbox, set the timeout to your team's real response-time expectations. A timeout that is too short turns a slow reviewer into a halted run.

Why There Is No On-Reject Branch

The Human node has a single output that fires only on approval. There is no separate "rejected" or "timed out" handle to drag an edge from, so you cannot build "if rejected, do X" branching directly off the node. This is intentional: an approval gate is meant to protect the steps that follow it, and a reject (or timeout) stops the run before those steps can execute.

If you have used the Condition node, this is a different shape. A Condition node forks the path with true and false outputs, and both branches stay in the same run. A Human node does not fork: a "no" ends the run rather than steering it. Keep the two ideas separate when you design. Use Condition for data-driven branching, and use Human when you genuinely want a non-approval to stop everything that comes after.

Designing Around a Halting Reject

Because the reject path halts, you build your "what happens on a no" logic before the gate, not after it. Here are the patterns that work in Spojit.

  • Notify before you gate, act after you gate. Put a Send Email or a Slack connector node (tool send-message) before the Human node to announce that a decision is needed, then place the protected action (for example a Connector node calling create-order or update-record) after the node so it only runs on approval. The "request sent" message always fires; the "done" action only fires on a yes.
  • Split the risky and reversible work. Do all the safe, reversible preparation (fetching records, building a draft, summarizing a document) before the gate. Reserve the irreversible action (charging a card, placing an order, deleting data) for nodes after the gate. A reject then leaves you in a clean, prepared-but-not-committed state.
  • Use a Subworkflow for the approved path. If the approved work is large, wrap it in a child workflow and call it with a Subworkflow node placed after the Human node. On a reject, the child never runs, and the halted parent shows clearly in execution history where the decision stopped it.
  • Let people record the reason for a no. Reviewers add their decision and any note in the Approvals inbox. Because a reject halts the run, treat that note as the record of why; you read it from approval history rather than acting on it downstream.
  • Ask Miraxa to wire it. Miraxa, the intelligent layer across your automation, can scaffold this layout for you. Try a prompt like "Add a Send Email node before my Human approval node that tells the approver a request is waiting, and connect the approval to a Connector node that creates the order." Miraxa adds and connects the nodes on the canvas, and asks first if anything is ambiguous.

Reading the Outcome in Execution History

Every approval outcome is visible after the fact. Open the workflow's execution history to see the run, and check the approval record to see whether it was Approved, Rejected, Timed out, or Cancelled, along with which slots were satisfied and any reviewer note. A halted run (from a reject or timeout) appears as a stopped execution at the Human node, which makes it easy to spot how many requests are not getting a yes and to tune your timeout or approver list accordingly.

Tips

  • Set a realistic Timeout (minutes). If approvers are emailed, allow hours; if they watch the Approvals inbox, match the timeout to your team's real response time so slow replies do not silently halt runs.
  • Remember that approval completes only when EVERY slot is satisfied. If one slot in a multi-slot gate is never satisfied, the node will eventually time out and halt, even if other slots already approved.
  • Keep all irreversible actions (charges, order creation, deletions) after the Human node so a reject or timeout leaves nothing half-done.
  • Use clear Notification title and Notification body text with {{ variables }} so approvers have enough context to decide before the timeout runs out.

Common Pitfalls

  • Trying to drag a "rejected" edge from the Human node. There is no reject output; a non-approval halts the run, so build your fallback messaging before the gate instead.
  • Treating a timeout as "still pending." A timeout is a final outcome (TIMEOUT) that halts the run just like a rejection. The run does not resume on its own when someone approves late.
  • Leaving Timeout (minutes) blank when you actually need the run to end if nobody responds. Blank means wait forever, which can leave a run paused indefinitely.
  • Putting the irreversible action (such as create-charge or delete-record) before the gate. If it runs before approval, a reject cannot undo it.

Related Articles

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.