Tutorial: let another system set stock through an incoming webhook

An Incoming webhook gives a workflow its own secret URL. Another system posts JSON to it, and the workflow runs with that JSON as its event. In this tutorial your ERP or warehouse sends { "sku": "ABC-123", "quantity": 12 }, and the workflow finds the variant and sets its stock. You will use the template that does exactly this, walk through its five steps, and try it from a terminal with curl.

You need: read and write access to products and inventory, and read access to locations, on the Permissions page.

1. Create the workflow

  1. Open Workflows, select Browse templates and search for SKU.
  2. Select Use template on Let another system set stock by SKU. The editor opens with the finished workflow.
  3. Select Save so the workflow gets its URL.
The canvas of Let the warehouse set stock by SKU: Incoming webhook, Find the variant by SKU, the condition A variant was found with Then and Otherwise, Read the first location and Set the quantity on Then, Stop: nothing found on Otherwise
The workflow: look the variant up, check it exists, read the location, set the quantity. An unknown SKU stops the run with a message.

2. Copy the URL and the example request

Select the trigger Incoming webhook at the top of the canvas. The side panel shows:

  • Webhook URL with a copy button. This is the address the other system posts to.
  • Example request: a complete curl command with the URL, the JSON header and a sample body, with its own Copy button.
  • A note that the event contains personal data, because whatever the caller sends is stored with the run.
The trigger panel of the Incoming webhook: a note that the event contains personal data, the Webhook URL with a copy button, and an Example request with a curl command; the secret part of the URL is hidden
The trigger panel. The last part of the URL is the secret (hidden here): anyone who has it can start this workflow.

3. How the steps read the request

The caller's JSON arrives under event.payload.body, its query string under event.payload.query and its headers under event.payload.headers. Select Find the variant by SKU to see the first step: a Shopify Admin API query that searches variants by SKU. Its Variables (JSON) builds the search from the body:

{ "q": {{ event.payload.body.sku | prepend: "sku:" | json }} }

The json filter turns the text into a valid JSON string. Under the query, the docs box shows what the query needs (read_products), a link to Shopify's documentation and its arguments.

The side panel of the step Find the variant by SKU: a Shopify Admin API (GraphQL) query editor, the badge Query: this step only reads and the start of the docs box for productVariants
The query step. Later steps read its answer as steps.variant.output.

Select the condition A variant was found. Its one rule checks that steps.variant.output.productVariants.nodes[0] has a value. If the search found nothing, the run takes the Otherwise branch and ends with Fail the run, whose message names the unknown SKU.

The Condition side panel of A variant was found: Rule 1 with the field steps.variant.output.productVariants.nodes[0] and the comparison has a value
The condition guards the rest of the workflow: no variant, no stock change.

Select Set the quantity, the last step. It is the ready-made action Set inventory quantity: Inventory item id and Location id come from the two queries before it, Quantity is {{ event.payload.body.quantity }}, and Reason is what Shopify shows in the inventory history. Under If it fails the step retries a temporary error automatically.

The side panel of the ready-made action Set the quantity: Inventory item id and Location id filled from earlier steps, Quantity from event.payload.body.quantity, and the note that Test this step runs the 3 steps before it first
The action that changes the stock. Every field is a variable from the event or from an earlier step.

4. Test it with a real SKU

  1. Select Test in the toolbar.
  2. Replace the Event (JSON) with a body that holds a real SKU of your store:
{ "body": { "sku": "ABC-123", "quantity": 12 } }
  1. Keep Preview (nothing is changed) and select Run preview. The two queries and the condition run for real; Set the quantity only reports what it would do. Select it to see the inventory item, the location and the quantity it would have set.

5. Turn it on and call it from a terminal

  1. Select Turn on. A workflow that is off answers the caller with an error and starts nothing.
  2. Paste the Example request from the trigger panel into a terminal and change the body:
bash
curl -X POST "<your webhook URL>" \
  -H "Content-Type: application/json" \
  -d '{"sku": "ABC-123", "quantity": 12}'
  1. Open Run history. The new run shows the trigger Incoming webhook, each step with its result, and the event the caller sent under Trigger event.

The caller gets an answer as soon as the run is accepted; it does not wait for the steps. Give the other system the URL, and it can set stock whenever it likes.

Make it yours

  • Several locations? Delete Read the first location and let the caller send a location_id in the body, or put a fixed Location id into the last step.
  • Guard the data. Add a rule to the condition: event.payload.body.quantity is at least 0, so a mistake on the other side cannot empty your stock.
  • Many SKUs in one request? Send { "items": [ ... ] } and wrap the steps in a Repeat for each over {{ event.payload.body.items }}. See Repeat for each.
  • Everything about the trigger, its answer and its limits: Incoming webhooks.