How to Wrap a Risky Action in Human Approval with a Timeout Fallback

Gate a high-value NetSuite record change behind a Human approval node with a timeout, then use a Condition on the approval outcome to alert Slack whenever the request times out or is rejected.

What This Integration Does

Some actions are too costly to run unattended. Updating a NetSuite credit limit, voiding a sales order, or changing a customer's payment terms are the kind of one-way changes you want a human to sign off on before Spojit commits them. This tutorial wraps a NetSuite update-record call behind a Human approval node so the change only happens once a named approver says yes, and it makes sure nobody silently lets a request sit forever by attaching a timeout.

The workflow runs on a Webhook trigger: an upstream system POSTs the record id and the proposed change, Spojit pauses at the Human node, and an approver acts in the Approvals inbox. If they approve, the NetSuite update runs. If they reject or the timeout elapses, the workflow halts at the Human node, so you place a Condition on a captured outcome variable to branch into a Slack alert that tells the team a request lapsed. Each run leaves an approval record and an execution-history entry you can audit later. Re-running the trigger starts a fresh approval; approvals are not reused across runs.

Prerequisites

  • A NetSuite connection with permission to read and update the record type you intend to change (test it with verify-connection).
  • A Slack connection with access to the channel you want alerts posted to.
  • A Webhook signing connection (scheme Spojit, Custom, or a vendor scheme) so the inbound POST is verified by HMAC. See setting up a webhook connection.
  • At least one approver set up as a workspace User, Role, or Team to fill an approval slot.
  • The Slack channel id (for example C0123ABCD) and the NetSuite record type and field you will change.

Step 1: Receive the request with a Webhook trigger

Add a Trigger node and set its type to Webhook. Choose your signing connection so Spojit verifies the HMAC signature on each call, then copy the generated URL into the upstream system. The trigger returns 202 with an executionId and exposes the parsed JSON body to later steps. Have the caller send the record id and the value you want to change, for example:

{
  "recordType": "customer",
  "recordId": "4827",
  "field": "creditlimit",
  "newValue": 50000,
  "requestedBy": "jordan@acme.com"
}

You can now reference these as {{ input.recordId }}, {{ input.field }}, {{ input.newValue }}, and {{ input.requestedBy }} downstream.

Step 2: Look up the current record for context

Add a Connector node in Direct mode, pick the NetSuite connector, and choose the get-record tool. Map recordType to {{ input.recordType }} and the internal id to {{ input.recordId }}. Store the result in an output variable such as currentRecord. This gives the approver a before-and-after picture: you can show both the existing value and the proposed value in the approval message, so they are not approving blind. Direct mode is deterministic and spends no AI credits, which is the right choice for a single predictable read.

Step 3: Pause for sign-off with a Human approval node

Add a Human node after the lookup. Fill in the fields:

  • Label: a short name such as Approve credit limit change.
  • Message: explain the change and include context, for example Raise credit limit for customer {{ input.recordId }} from {{ currentRecord.creditlimit }} to {{ input.newValue }}, requested by {{ input.requestedBy }}.
  • Timeout (minutes): set a real deadline, for example 240 for four hours. A blank value means no timeout, which is exactly what you want to avoid here.
  • Notification title/body: these support {{ variables }}, so reuse the same record and value references.
  • Urgency: set to High for a financial change.
  • Email approvers: turn on if you want the first ten approvers emailed in addition to the in-app notification.
  • Approval slots: the only required field. Add a slot and put your approver in it as a User, Role, or Team atom. Approval completes when every slot is satisfied (AND across slots; any atom satisfies its own slot).

When the run reaches this node it pauses and surfaces the request in the Approvals inbox at /approvals, the dashboard widget, and the menu badge. On approval the node continues and outputs { approved: true, approvalId, outcome: "APPROVED" }. On rejection or timeout the workflow halts at this node.

Step 4: Capture the outcome so a Condition can branch on it

Because a rejected or timed-out approval halts the workflow, you cannot read a "rejected" branch straight off the Human node. The pattern is to record progress in a variable that survives the halt. Add a Transform node immediately after the Human node and set a status variable, for example approvalStatus = "APPROVED", stored in an output variable such as gateState. Because this Transform only runs when the Human node continues, the presence of {{ gateState.approvalStatus }} being APPROVED is your signal that sign-off succeeded. If the approval times out or is rejected, the run stops before this Transform ever sets the value, and the execution-history entry records the TIMEOUT or APPROVAL_NOT_APPROVED outcome for the alert step described in Step 6 to reference.

Step 5: Run the NetSuite change only on the approved path

After the Transform, add a Condition node that checks {{ gateState.approvalStatus }} equals APPROVED. On the true branch, add a Connector node in Direct mode using the NetSuite connector and the update-record tool. Map the inputs from the original request:

{
  "recordType": "{{ input.recordType }}",
  "recordId": "{{ input.recordId }}",
  "fields": {
    "{{ input.field }}": {{ input.newValue }}
  }
}

Store the result in updateResult. This is the risky action, now guarded so it only fires once a human has explicitly approved. Keep it in Direct mode so the exact field and value you approved are the field and value that get written, with no AI interpretation in between.

Step 6: Alert Slack when the approval lapses or is rejected

To turn the silent halt into a visible signal, split the alerting into two complementary pieces. First, on the false branch of the Step 5 Condition (or any path where you can branch before the halt), you will not get control after a timeout, so the reliable place to notify on a lapsed or rejected gate is the workflow's failure handling. Add a Connector node using the Slack connector in Direct mode with the send-message tool, mapping channel to your channel id (for example C0123ABCD) and text to a clear message such as:

Approval gate not cleared for customer {{ input.recordId }}. Requested change to {{ input.field }} = {{ input.newValue }} by {{ input.requestedBy }} was rejected or timed out. NetSuite was NOT updated.

Configure this Slack alert on the rejection-and-timeout path: because a rejected or timed-out Human node halts the run, route this notification through your workflow's error handling so the team is told the change did not go through. For the approved path, you can optionally add a second Slack send-message after update-record confirming the change landed, referencing {{ updateResult }}. See handling errors and building fallbacks and understanding retry and error handling for wiring the halt path to an alert.

Step 7: Optionally confirm by email as well

If approvers live outside Slack, add a Send Email node on the approved path that emails the requester. Set Recipients to {{ input.requestedBy }}, a templated Subject like Credit limit approved for {{ input.recordId }}, and a short body referencing {{ input.newValue }}. The Send Email node uses Spojit's built-in mail service and needs no connection, though external recipients must be on your org allowlist under Settings > General > Email recipients. You can ask Miraxa, the intelligent layer across your automation, to scaffold this branch for you, then fine-tune the fields in the properties panel.

Tips

  • Choose a timeout that matches how fast the change actually needs to happen. Four hours for a credit limit is reasonable; for an urgent void you might use 30 minutes and set Urgency to High.
  • Put context in the approval Message using the currentRecord lookup so approvers see the before value next to the proposed value and never approve blind.
  • Use multiple Approval slots when you need two-person sign-off: every slot must be satisfied, giving you AND semantics across roles or teams.
  • Keep both NetSuite calls in Direct mode so the approved field and value are written exactly, with no AI reinterpretation between approval and execution.

Common Pitfalls

  • Expecting an "on reject" branch off the Human node. Rejection and timeout halt the workflow; they do not produce a continue path. Detect them through error handling and the captured status variable, not a node output.
  • Leaving Timeout (minutes) blank. With no timeout a request can sit in the inbox indefinitely and the risky action never resolves either way. Always set a deadline for a gated action.
  • A NetSuite connection without update permission on the target record type. The read may succeed while update-record fails; verify with verify-connection and the specific record type first.
  • Posting to a Slack channel the connection cannot access. Confirm the channel id and that the Slack connection has been invited to the channel before relying on the alert.

Testing

Validate on a low-stakes record first. Point the webhook at a sandbox NetSuite record or a non-sensitive field, set a short timeout such as 5 minutes, and send a test POST. Approve the request in the Approvals inbox and confirm the update-record call runs and writes the value. Then run it again and deliberately let it time out: confirm the Slack alert fires and that NetSuite was not changed. Finally test the reject path the same way. Only widen to production records and a realistic timeout once all three outcomes behave as expected.

Learn More

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