How to Process Adobe Commerce Order Webhooks into NetSuite Customers and Sales Orders

Receive an Adobe Commerce order webhook the moment a customer checks out, upsert the buyer as a NetSuite customer, then create the matching NetSuite sales order in the same run.

What This Integration Does

When a shopper places an order in your Adobe Commerce storefront, finance and fulfilment teams need that order to land in NetSuite as a real customer and a real sales order, without anyone re-keying it. This Spojit workflow does exactly that in real time: Adobe Commerce posts an order event to a Spojit webhook, Spojit fetches the full order, makes sure the buyer exists as a NetSuite customer (creating them if they do not), and then writes a NetSuite sales order linked to that customer. The result is a clean, idempotent bridge from your storefront to your ERP that runs in seconds.

The run is triggered by an HTTP POST from Adobe Commerce (or a thin notifier in front of it) to the workflow's webhook URL, verified with HMAC through a signing connection. The webhook payload only needs to carry the Adobe Commerce order ID; the workflow then calls Adobe Commerce to pull the authoritative order, so it never trusts unverified body data. Each run leaves one NetSuite customer (reused on repeat orders from the same email) and one new NetSuite sales order. Re-runs of the same order are safe as long as you key the customer lookup on email and guard against duplicate sales orders, which the Tips section covers.

Prerequisites

  • An Adobe Commerce REST connection authorized for your store, with permission to read orders and customers (tools used: get-order, get-customer).
  • A NetSuite connection with access to the customer and salesOrder record types and SuiteQL/REST read access (tools used: list-customers, create-customer, create-record).
  • A Webhook signing connection (scheme Custom or Spojit) so Spojit can verify the incoming POST. See the connection setup guide linked below.
  • Your NetSuite subsidiary internal ID, and a mapping from Adobe Commerce store/currency to the NetSuite items you sell, so order lines resolve to NetSuite item internal IDs.

Step 1: Receive the order with a Webhook trigger

Add a Trigger node and set its type to Webhook. Pick your signing connection so Spojit verifies the HMAC signature on every request and rejects anything unsigned. Copy the generated workflow URL into your Adobe Commerce order notification (a webhook subscription or a small forwarder that fires on sales_order_place). The trigger responds 202 with an executionId and exposes the parsed JSON body as {{ input }}. Keep the payload minimal, for example:

{
  "event": "order.placed",
  "orderId": 100000457
}

Turn on event-id deduplication using the order ID header if your sender can retry, so replays do not create duplicate runs.

Step 2: Fetch the authoritative order from Adobe Commerce

Add a Connector node in Direct mode, choose the Adobe Commerce REST connector, and select the get-order tool. Map its order identifier field to {{ input.orderId }}. This returns the canonical order, including the billing email, billing address, line items, totals, and currency. Working from this fetched record (rather than the webhook body) means a tampered or partial POST can never drive a bad sales order. Store the result in an output variable such as order so later steps can read fields like {{ order.customer_email }} and {{ order.items }}.

Step 3: Look up the buyer in NetSuite

Add a second Connector node in Direct mode on the NetSuite connector and select list-customers. Use the q filter to find an existing customer by the Adobe Commerce billing email, for example:

email IS "{{ order.customer_email }}"

Save the result to customerLookup. If a record comes back, you reuse its internal ID; if the list is empty, the next step creates one. This lookup is what makes repeat buyers map to a single NetSuite customer instead of a new record per order.

Step 4: Upsert the customer with a Condition

Add a Condition node that checks whether the lookup found anyone, for example whether the count of {{ customerLookup.items }} is greater than 0.

On the false branch (no match), add a Connector node in Direct mode using the NetSuite create-customer tool. Map the body from the Adobe Commerce order so the new customer carries the right name, email, and subsidiary:

{
  "isPerson": true,
  "firstName": "{{ order.billing_address.firstname }}",
  "lastName":  "{{ order.billing_address.lastname }}",
  "email":     "{{ order.customer_email }}",
  "phone":     "{{ order.billing_address.telephone }}",
  "subsidiary": { "id": "1" }
}

On the true branch, simply carry the matched internal ID forward. Use a Transform node after the condition to converge both branches into a single variable, for example customerId, holding either the existing ID or the newly created one. That single value keeps Step 5 clean.

Step 5: Create the NetSuite sales order

Add a final Connector node in Direct mode on the NetSuite connector and select the create-record tool. Set recordType to salesOrder and build the body by mapping the Adobe Commerce order onto the NetSuite sales order schema: the customer entity, the subsidiary, and an item line for each row in {{ order.items }}. A typical body looks like:

{
  "entity":     { "id": "{{ customerId }}" },
  "subsidiary": { "id": "1" },
  "memo":       "Adobe Commerce order {{ order.increment_id }}",
  "item": {
    "items": [
      {
        "item":     { "id": "{{ line.netsuiteItemId }}" },
        "quantity": "{{ line.qty_ordered }}",
        "rate":     "{{ line.price }}"
      }
    ]
  }
}

Because each Adobe Commerce SKU must resolve to a NetSuite item internal ID, use a Loop (ForEach) over {{ order.items }} with a Transform step to map each SKU before assembling the line array. Save the created order to salesOrder so you can reference its internal ID downstream.

Step 6: Confirm and notify

Add a Send Email node so your operations inbox gets a record of every synced order. Set the recipients, a subject such as NetSuite SO created for {{ order.increment_id }}, and a body that includes the new NetSuite sales order ID from {{ salesOrder }}. Choose Continue anyway for "If sending fails" so a mail hiccup never rolls back a successful order sync. Remember that external recipients must be on your org allowlist under Settings -> General -> Email recipients.

Tips

  • Guard against duplicate sales orders: before Step 5, run a NetSuite run-suiteql query for an existing sales order whose memo or external ID matches {{ order.increment_id }}, and skip creation if one already exists. This makes webhook replays harmless.
  • Set the sales order externalId to the Adobe Commerce increment ID. NetSuite enforces uniqueness on external IDs, giving you a second line of defence against duplicates.
  • Resolve SKUs to NetSuite item internal IDs once and cache the map in a Transform node or a small lookup, rather than calling NetSuite per line, to stay well under API rate limits on busy stores.
  • Use Miraxa, the intelligent layer across your automation, to scaffold this canvas from a sentence such as "Build a workflow that takes an Adobe Commerce order webhook, looks up or creates the NetSuite customer, then creates a NetSuite sales order", then fine-tune each node in the properties panel.

Common Pitfalls

  • Trusting the webhook body. Always re-fetch the order with get-order; do not build the sales order from the raw POST, which is easy to spoof or send partially.
  • Missing subsidiary. NetSuite rejects customer and sales order creation in OneWorld accounts when subsidiary is absent. Hardcode or map the correct internal ID.
  • Unmapped SKUs. If an Adobe Commerce SKU has no NetSuite item, create-record fails on that line. Validate the SKU map in the loop and route unmapped lines to a Human approval or an alert email instead of failing the whole order.
  • Email casing and whitespace. The list-customers filter is literal, so trim and lower-case {{ order.customer_email }} in a Transform before the lookup, or you will create duplicate customers for the same person.

Testing

Start on a single, low-value test order in an Adobe Commerce staging store. Send one webhook by placing that order (or POSTing the minimal body in Step 1 manually), and watch the run in execution history. Confirm Step 2 returns the full order, Step 3 either finds or misses the customer as expected, Step 4 leaves exactly one NetSuite customer, and Step 5 creates one sales order with correct lines and totals. Re-send the same webhook to prove your duplicate guard (external ID or SuiteQL check) prevents a second sales order. Only once a handful of test orders flow cleanly should you point production Adobe Commerce traffic at the webhook URL.

Learn More

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