Webhooks
An overview of Hookr, the WinShop service that pushes change events to your endpoint. Available on request.
Available on request
Webhooks are not switched on by default and cannot be configured through the API. WinShop support sets them up for each customer and each integration. Write to [email protected] to ask for them.
Polling works for every integration, but it is always a little late and it costs requests even when nothing changed. For data that has to be fresh, WinShop can instead tell you when something changes. The component that does this is called Hookr.
How it works
- A record changes in the WinShop database, for example a stock quantity or an order.
- WinShop records an event for the change.
- Hookr sends the event to your HTTPS endpoint as an HTTP
POSTwith a JSON body. - Your endpoint answers with a
2xxstatus to confirm it has the event.
There is no self-service API for subscriptions. You tell support which kinds of events you want and where to send them; support creates the subscription on the customer's instance. The list of available event types and the exact layout of the event body are agreed at that point, because they depend on what the instance has configured.
What you need to provide
- A public HTTPS endpoint that accepts
POSTrequests with a JSON body. - A way for WinShop to authenticate to you. WinShop can call your endpoint with an OAuth 2.0 client-credentials token: you give support a token URL, a client id and a client secret, WinShop fetches a token from your token endpoint and sends it as
Authorization: Bearer …. Your endpoint validates that token like any other. Note the direction: this is a token issued by your system to WinShop, the opposite of the token you use to call the WinShop API.
Delivery is at least once
If your endpoint does not confirm an event, WinShop sends it again. That means the same event can arrive more than once. Do not rely on the order of arrival either.
Write your handler so that receiving an event twice does no harm:
- Deduplicate. If the events you receive carry a unique identifier, store the identifiers you have handled and skip repeats.
- Treat the event as a signal, not as the data. When an event says a record changed, read the current state of that record from the API and store that. Applying the same read twice gives the same result.
- Confirm fast. Answer
2xxas soon as you have stored the event, and do the real work afterwards. A slow answer can be treated as a failure and cause a resend.
A minimal receiver that does all three:
import { createServer } from "node:http";
// use a database table in production
const handled = new Set<string>();
const queue: unknown[] = [];
createServer((request, response) => {
if (request.method !== "POST") {
response.writeHead(405).end();
return;
}
if (!isValidToken(request.headers.authorization)) {
response.writeHead(401).end();
return;
}
let body = "";
request.on("data", (chunk) => (body += chunk));
request.on("end", () => {
let event: unknown;
try {
event = JSON.parse(body);
} catch {
response.writeHead(400).end();
return;
}
// the deduplication key is agreed with support when the subscription is set up
const key = eventKey(event);
if (!handled.has(key)) {
handled.add(key);
queue.push(event); // process later: re-read the changed record from the API
}
response.writeHead(204).end();
});
}).listen(8080);
function isValidToken(header: string | undefined): boolean {
// validate the Bearer token your own token endpoint issued
return header?.startsWith("Bearer ") ?? false;
}
function eventKey(event: unknown): string {
return JSON.stringify(event);
}Keep a safety net
Webhooks reduce latency; they do not replace reconciliation. Keep a periodic sync that catches anything a webhook missed, for example a nightly run of the incremental loops in the Product catalog sync and Stock levels sync guides. With webhooks in place that sync can run far less often.