Algemene webhook: verkopen doorsturen naar Stokyo
Met de algemene webhook stuurt elke kassa, webshop of integrator verkopen naar Stokyo. Stokyo trekt ze automatisch van de voorraad af. Deze pagina is bedoeld voor wie de koppeling bouwt.
- 1. Adres en geheime sleutel
- 2. Een verkoop versturen
- 3. Velden
- 4. Ondertekenen (HMAC-SHA256)
- 5. Retours en storno’s
- 6. Antwoorden
- 7. Goed om te weten
1. Adres en geheime sleutel
De uitbater vindt beide in Stokyo onder Meer → Koppelingen → Algemene koppeling → Adres en sleutel tonen. Elke zaak krijgt een eigen adres (eindigt op /pos-webhook/ep_…) en een eigen geheime sleutel (begint met whsec_). Bewaar de sleutel alleen op de server, nooit in een app of webpagina.
2. Een verkoop versturen
Stuur een POST met een JSON-body. Je mag één event sturen (zonder events-lijst) of tot 100 events tegelijk.
POST https://tcphaxqfzdfgvkxixbzk.supabase.co/functions/v1/pos-webhook/ep_…
Content-Type: application/json
Stokyo-Signature: t=1793400000,v1=5f2b…c91e
{
"events": [
{
"ticketId": "1287",
"type": "sale",
"occurredAt": "2026-10-30T21:14:00+01:00",
"lines": [
{ "itemId": "PILS25", "name": "Pils 25 cl", "qty": 2, "unitPrice": 3.20, "barcode": "5410228142218" },
{ "itemId": "MOJ", "name": "Mojito", "qty": 1, "unitPrice": 9.50 }
]
}
]
}3. Velden
| Veld | Verplicht | Uitleg |
|---|---|---|
ticketId | ja | Unieke id van het ticket in de kassa. Hetzelfde ticket opnieuw sturen boekt niets dubbel. |
type | nee | sale (standaard), refund (retour) of void (storno van een heel ticket). |
occurredAt | nee | ISO 8601-tijdstip van de verkoop, met tijdzone. Leeg = nu. |
refundOf | nee | Bij refund of void: het oorspronkelijke ticketId. |
lines[].itemId | één van drie | Artikel-id in de kassa (PLU, SKU). Stokyo onthoudt aan welk product dit gekoppeld is. |
lines[].barcode | één van drie | EAN-code. Komt die overeen met een product in Stokyo, dan wordt automatisch gekoppeld. |
lines[].name | één van drie | Naam zoals op het ticket. Gebruikt voor voorstellen bij het koppelen. |
lines[].qty | ja | Aantal, groter dan 0. Ook bij een retour positief. |
lines[].unitPrice | nee | Verkoopprijs per stuk, ter info. |
4. Ondertekenen (HMAC-SHA256)
Elke aanvraag heeft een header Stokyo-Signature: t=<unix-seconden>,v1=<hex>. v1 is de HMAC-SHA256 van <t>.<ruwe body> met de geheime sleutel. Onderteken exact de bytes die je verstuurt. Het tijdstip mag maximaal 5 minuten afwijken; zo kan een onderschepte aanvraag later niet opnieuw gebruikt worden.
Node.js
import crypto from "node:crypto";
const body = JSON.stringify(payload); // send exactly this text
const t = Math.floor(Date.now() / 1000);
const v1 = crypto.createHmac("sha256", SECRET).update(`${t}.${body}`).digest("hex");
await fetch(URL, {
method: "POST",
headers: { "Content-Type": "application/json", "Stokyo-Signature": `t=${t},v1=${v1}` },
body,
});Python
import hmac, hashlib, json, time, requests
body = json.dumps(payload, separators=(",", ":"))
t = int(time.time())
v1 = hmac.new(SECRET.encode(), f"{t}.{body}".encode(), hashlib.sha256).hexdigest()
requests.post(URL, data=body, headers={
"Content-Type": "application/json",
"Stokyo-Signature": f"t={t},v1={v1}",
})Shell (curl)
BODY='{"events":[{"ticketId":"T-1","lines":[{"itemId":"PILS25","qty":1}]}]}'
T=$(date +%s)
SIG=$(printf '%s' "$T.$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -X POST "$URL" -H "Content-Type: application/json" \
-H "Stokyo-Signature: t=$T,v1=$SIG" --data "$BODY"5. Retours en storno’s
Een refund zet de vermelde aantallen terug in de voorraad. Een void draait alles terug wat van het oorspronkelijke ticket geboekt werd. Komt een storno binnen vóór het ticket zelf, dan wacht Stokyo en verwerkt ze zodra het ticket er is.
{ "events": [
{ "ticketId": "1288", "type": "refund", "refundOf": "1287",
"lines": [ { "itemId": "PILS25", "qty": 1 } ] },
{ "ticketId": "1287", "type": "void" }
] }6. Antwoorden
200 betekent: veilig bewaard. Het antwoord vertelt wat er gebeurde:
{ "ok": true, "accepted": 1, "duplicates": 0, "booked": 1, "needsMapping": 0, "waiting": 0, "queuedForRetry": 0 }duplicates: tickets die al binnen waren (niets dubbel geboekt).needsMapping: tickets met artikelen die Stokyo nog niet kent. Die staan in de app bij Nog te koppelen en worden geboekt zodra de uitbater ze koppelt.queuedForRetry: tijdelijk niet verwerkt; Stokyo probeert het zelf opnieuw.
| Code | Betekenis | Wat doen |
|---|---|---|
| 401 | Handtekening ontbreekt, klopt niet of is te oud | Controleer sleutel en klok. Niet opnieuw sturen zonder aanpassing. |
| 404 | Onbekend adres | Adres opnieuw kopiëren uit Stokyo. |
| 400 / 422 | Geen geldige JSON of foute velden (details zegt welke) | Body aanpassen. |
| 413 | Te groot (meer dan 100 events of 1 MB) | Opsplitsen. |
| 503 | Tijdelijk niet beschikbaar | Opnieuw proberen, bv. na 1, 5 en 30 minuten. Dubbel sturen kan geen kwaad. |
7. Goed om te weten
- Samengestelde producten (een cocktail of menu) stel je in Stokyo zelf in: één kassa-artikel kan zo meerdere voorraadproducten verminderen.
- Een nieuwe sleutel maken in Stokyo maakt de oude meteen ongeldig.
- Vragen of een rechtstreekse koppeling voorstellen: contact@stokyo.app.