Skip to main content

Notifications

Wino sends a POST request with a JSON body to your URL:

{
"kind": "Variant",
"source": {
"shopId": "20258224-05f3-49a7-9299-fab4acb89ee5",
"deviceId": "b1fd5f30-a879-40a0-9a86-46804c16ed12"
},
"data": {
"id": "4e584c64-a377-4689-aa01-7fbd32f12a44",
"…": "the properties of the variant"
},
"clientState": "<a secret of yours>"
}
FieldDescription
kindThe kind of data that changed: one of the settings names. It doesn't say what happened (created, updated, archived): fetch the record to see its current state
source.shopIdThe shop where the change happened
source.deviceIdThe device the change is reported from. For changes made through the API, it's the shop's Web device (slug W), not your device: it doesn't tell you who made the change
dataThe record after the change. Its properties depend on kind and use Wino's internal field names, not always the GraphQL ones. Its dates are timestamps in milliseconds (GraphQL returns ISO dates). data.id is always there
clientStateThe value you set on the subscription

Other properties may appear: ignore the ones you don't use.

Receive notifications​

  1. Answer fast with a 2xx status. Wino waits 10 seconds at most. Save the notification and process it afterwards (a queue, a background job).
  2. Check clientState against the secret you set, and ignore notifications that don't match.
  3. Use HTTPS, and give the final URL. If your URL redirects (for example from http to https), the notification arrives as a GET without body.
  4. Fetch the current data with the GraphQL API from data.id, rather than relying only on data: it's the safest way to get related records too.
  5. Expect duplicates and any order. When a delivery fails (an error status, or no answer within 10 seconds), Wino sends the notification again to every subscriber of the shop, and keeps trying for a while: you can receive it again even after answering 2xx. If your URL keeps failing, Wino contacts you by email. Process the same notification twice without harm, for example by comparing data.updatedAt (in milliseconds) with the last one you processed. A stock movement never changes: skip a StockActivity whose data.id you already have.
  6. Don't write back what you already have. You're also notified of the changes your integration makes. Before writing a change back to Wino, compare it with your own data, and do nothing if it's the same: that's what prevents loops (you update the stock, get notified, update it again…). For stock movements (StockActivity), data.deviceId is the device the movement was recorded with: your device ID for the movements you create.

In these receivers, WINO_CLIENT_STATE holds the clientState of your subscription. They refuse to work without it: otherwise a request with no clientState would pass the check. queue stands for your own queue or job system.

import express from "express";

const clientState = process.env.WINO_CLIENT_STATE;
if (!clientState) throw new Error("Set WINO_CLIENT_STATE before starting");

const app = express();
app.use(express.json({ limit: "5mb" })); // the default 100 kB is too small for some notifications

app.post("/wino-webhooks", (req, res) => {
const notification = req.body;
if (notification.clientState !== clientState) {
return res.sendStatus(401);
}
queue.push(notification); // your queue or job system: process it later, outside the request
res.sendStatus(200);
});

app.listen(3000);