How to Send NetSuite Record Events to a Workflow (NetSuite Webhooks)

Start a Spojit workflow the moment a NetSuite record is created or changed, such as a sales order approved, an item fulfilled or a customer updated, using a SuiteScript user event script that posts a signed webhook.

What This Integration Does

NetSuite has no general-purpose outbound webhooks: there is no settings page where you paste a URL and pick events. You have two options. You can poll, running a scheduled SuiteQL query for records modified since the last run, which needs no NetSuite customisation and is fine for anything that can wait a few minutes. Or you can push, deploying a small SuiteScript user event script that runs after a record is saved and posts it to a Spojit webhook URL. This tutorial covers the push option, with a signed request so Spojit only accepts events that really came from your account. The polling option is described at the end.

Each save of a matching record sends one webhook, and each webhook starts one Spojit run. The script sends an event ID made from the record ID and its last-modified time, so if NetSuite or the network retries the same event, Spojit deduplicates it instead of running the workflow twice.

Prerequisites

  • NetSuite administrator access, with Server SuiteScript enabled (Setup -> Company -> Enable Features -> SuiteCloud).
  • A Spojit workflow with a Webhook trigger and a Custom signing connection (created in Step 1).
  • A NetSuite connection in Spojit if the workflow needs to read more of the record or write back.

Step 1: Create the Webhook Trigger

Add a Trigger node with type Webhook. Next to Signing connection, click +, pick Custom and set:

  • Signature header name: X-NetSuite-Signature
  • Algorithm: SHA-256
  • Encoding: hex
  • Signed payload: Raw body
  • Event ID header: X-NetSuite-Event-Id

Enter a long random signing secret and save. Copy the webhook URL from the trigger panel. Leave the response mode on Async so NetSuite gets an immediate 202 and the save is not held up by your workflow.

Step 2: Store the Secret in NetSuite

In NetSuite, go to Setup -> Company -> API Secrets and create a secret with the same value, with the ID custsecret_spojit_webhook. Restrict it to the script you deploy in Step 3. The script refers to the secret by ID, so the value never appears in script code.

Step 3: Deploy a User Event Script

Create a SuiteScript 2.1 file with an afterSubmit entry point. This example sends sales orders when they are created or edited:

/**
 * @NApiVersion 2.1
 * @NScriptType UserEventScript
 */
define(['N/https', 'N/crypto', 'N/encode', 'N/log'], (https, crypto, encode, log) => {
  const URL = 'https://YOUR-SPOJIT-WEBHOOK-URL';

  const afterSubmit = (context) => {
    if (context.type !== context.UserEventType.CREATE &&
        context.type !== context.UserEventType.EDIT) return;
    const rec = context.newRecord;
    const body = JSON.stringify({
      event: 'salesorder.' + context.type,
      id: rec.id,
      tranid: rec.getValue('tranid'),
      status: rec.getValue('status'),
      entity: rec.getValue('entity'),
      total: rec.getValue('total'),
      lastModified: rec.getValue('lastmodifieddate')
    });

    const hmac = crypto.createHmac({
      algorithm: crypto.HashAlg.SHA256,
      key: crypto.createSecretKey({ secret: 'custsecret_spojit_webhook', encoding: encode.Encoding.UTF_8 })
    });
    hmac.update({ input: body, inputEncoding: encode.Encoding.UTF_8 });
    const signature = hmac.digest({ outputEncoding: encode.Encoding.HEX });

    try {
      https.post({
        url: URL,
        body: body,
        headers: {
          'Content-Type': 'application/json',
          'X-NetSuite-Signature': signature,
          'X-NetSuite-Event-Id': rec.id + '-' + rec.getValue('lastmodifieddate')
        }
      });
    } catch (e) {
      log.error('Spojit webhook failed', e.message);
    }
  };

  return { afterSubmit };
});

Upload it to the File Cabinet, create a script record, and deploy it to the Sales Order record type with the status Released. Deploy the same script to other record types (Item Fulfillment, Customer, Invoice) by changing the fields it sends.

Step 4: Build the Workflow

The event arrives as {{ input }}. A typical flow:

  1. A Condition on {{ input.status }} so only the statuses you care about continue (for example "Pending Fulfillment").
  2. A NetSuite step with get-sales-order and {{ input.id }} to load the lines and addresses, because the webhook only carries the header fields.
  3. The action: create a shipment with your shipping connector, notify the warehouse in Slack, or update the order in your store.

Step 5: Handle Failures

The script logs an error if the post fails, but it does not retry, so a Spojit outage or a network blip can drop an event. Pair the webhook with a nightly safety net: a scheduled workflow that runs run-suiteql for orders modified in the last day and processes any the webhook missed. Because the main workflow is keyed on the record ID, processing an order twice should be harmless; check for an existing shipment or flag before acting.

Tips

  • Keep the script light: the HTTP call runs inside the user's save. Send a compact payload and let the workflow fetch the rest, so saves stay fast.
  • Skip script-driven saves: load the N/runtime module, check runtime.executionContext and return early for CSV imports or other integrations if you only want events from users in the UI.
  • Polling alternative: if you cannot deploy scripts, schedule run-suiteql every 5 minutes with WHERE lastmodifieddate >= ... and a small overlap. It costs one query per run and needs no NetSuite changes.
  • Test in a sandbox: deploy the script to a sandbox account first, with a separate Spojit signing connection and workflow.

Common Pitfalls

  • Signature mismatch: the script must sign exactly the string it sends. Build body once and use the same variable for the HMAC and the request.
  • Secret not available to the script: API secrets restricted to other scripts fail with an error at createSecretKey. Allow the deployed script on the secret.
  • Governance units: each https.post uses governance units. Bulk CSV imports that save thousands of records can exhaust them; skip non-UI contexts for bulk jobs.
  • Webhook not firing for inline edits: some changes (mass updates, scheduled scripts) run in contexts you may have excluded. Check the deployment's Execution Context settings if events seem missing.

Testing

Save a test sales order in the sandbox and open the workflow's run history in Spojit: a run should appear within seconds with the order in {{ input }}. Edit and save it again to check that a second event arrives with a new event ID, then deploy to production.

Learn More

Ready to build this?

Start on the free plan: 500 AI credits a month, no card needed.

Start free

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