---
title: "Tutorial: let another system set stock through an incoming webhook"
description: "Give your ERP or warehouse a secret URL. Every POST with a SKU and a quantity looks the variant up and sets its stock. With the curl command to try it from a terminal."
canonical: "https://docs.workflow-suite.app/tutorial-set-stock-through-an-incoming-webhook"
---

# 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](https://cdn-dev.eu.codecreationlabs.cloud/crm-tool/uploads/SjJ78gKLnLlocopCui3Cs62dJbGZ7F1n/9b73334ff63bb97a.png)
*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](https://cdn-dev.eu.codecreationlabs.cloud/crm-tool/uploads/SjJ78gKLnLlocopCui3Cs62dJbGZ7F1n/4efdfa4ceb0ea2e4.png)
*The trigger panel. The last part of the URL is the secret (hidden here): anyone who has it can start this workflow.*

> [!WARNING]
> Treat the URL like a password. Do not paste it into tickets, chats or screenshots. If it leaks, duplicate the workflow: the copy gets a new URL, and you delete the old one.

## 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](https://cdn-dev.eu.codecreationlabs.cloud/crm-tool/uploads/SjJ78gKLnLlocopCui3Cs62dJbGZ7F1n/a445cac7723bd3b4.png)
*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](https://cdn-dev.eu.codecreationlabs.cloud/crm-tool/uploads/SjJ78gKLnLlocopCui3Cs62dJbGZ7F1n/b79fe62a326cdaf7.png)
*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](https://cdn-dev.eu.codecreationlabs.cloud/crm-tool/uploads/SjJ78gKLnLlocopCui3Cs62dJbGZ7F1n/a586ecf7a633febd.png)
*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 } }
```

3. 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}'
```

3. 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](https://docs.workflow-suite.app/repeat-for-each.md).
- Everything about the trigger, its answer and its limits: [Incoming webhooks](https://docs.workflow-suite.app/incoming-webhooks.md).
