Süper GelsinPartner APIv1.0.0
API Referansı
TREN
Başlangıç
  • Hemen Başla
  • Test Ortamı (Sandbox)
  • Yetkilendirme
  • Servis Limitleri
  • Test hesabı başvurusu
Ortak uçlar
  • İşletme
  • Sipariş Yaşam Döngüsü
  • Sipariş Durumları
  • Mali Belge
  • Kurye Firması Entegrasyonu
  • Müşteri Talepleri
  • Değerlendirmeler
  • Webhook
Yemek
  • Menü ve Ürün Eşleme
Çarşı
  • Çarşı'ya genel bakış
  • Katalog
  • Toplama ve Dönüş
Referans
  • Sürümleme ve Hız Sınırı
  • Hata Kodları ve Sorun Giderme
  • Changelog
  • Sıkça Sorulan Sorular
  • API Referansı
Developer Portal/Shared endpoints

Webhooks

Registering the webhook address, real-time order notifications, signature verification, deduplication and retries.

Endpoints on this page

Each row links to the full definition in the API reference — fields, response codes and the try-it console.

  • GET/webhook
  • PUT/webhook
  • DELETE/webhook
  • POST/webhook/test

Events

AlanTipAçıklama
order.createdeventA new order.
order.updatedeventA status change (restaurant: preparing, ready, on_the_way, delivered; Çarşı: confirmed, preparing, on_the_way, delivered). On a restaurant, an undeliverable order paid online also arrives with this event (deliveryFailed.completedSale: true); it is not a cancellation.
order.cancelledeventOrder cancellation (by the customer, support or the business). On Çarşı, an undeliverable order also arrives with this event.
Cancellation is a separate event; it is not folded into order.updated. If you do not handle it, the cancelled order stays open in your point of sale. On a restaurant, an undeliverable order paid online does not count as a cancellation: it arrives with order.updated and is closed as a sale.

Body: {eventId, event, sentAt, order}. The order field is the same object as the GET /orders/{id} response. Read the event type from the event field in the body; headers are outside the signature. A status written by the point-of-sale software itself is not sent back as a webhook.

Registering the address

Only this API writes the webhook address; it appears read-only in the business console and in the mobile app. PUT /webhook registers the address for the business the key is bound to (the same endpoint on both verticals); GET /webhook reads it and DELETE /webhook removes the registration (it returns 200 even if there is none), after which delivery stops. Without a webhook, order events can be followed only with GET /orders.

Registering the address
curl -X PUT "https://api.supergelsin.com/api/partner/v1/webhook" \
  -H "Authorization: Basic $(printf '%s:%s' "$API_KEY" "$API_SECRET" | base64)" \
  -H "X-Integrator: Ornek POS" \
  -H "X-Operator: kasiyer_01" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://ornek-entegrator.example/hook", "events": ["order.created", "order.updated", "order.cancelled"] }'
AlanTipAçıklama
urlzorunlustringA public address starting with https://. Private network addresses, localhost and redirecting addresses are rejected (400 invalid_webhook_url).
secretstringThe signature secret. It must be the whsec_ prefix followed by standard base64 (padded) that decodes to 24–64 bytes. If omitted, the existing valid secret is kept; if there is none, or the existing one is not in a valid format, a new secret is generated.
eventsarraySubscribed events; if omitted, the existing setting, or all three if none.
authHeader · authValuestring | nullThe receiver's own authentication header (for example Authorization); added to every request. It cannot be webhook-id, webhook-timestamp, webhook-signature or content-type.

A generated secret is returned in the clear only in the PUT response (secret and secretNote); afterwards it appears masked (whsec_••••abcd). If it is lost, renew it with another PUT. The body can be at most 16 KB; beyond that 413 payload_too_large is returned. GET /webhook also returns the trace of the last delivery attempt in lastDelivery (at, status: ok, error, idle).

No event is sent to a configuration without a secret, or with a secret in an invalid format; the business console shows "webhook imza sırrı tanımlı değil" (webhook signature secret not defined). Because each business has its own key, the address of each storefront is written with that storefront's own key; you may give the same address.

Deduplication

Delivery is at-least-once, not exactly-once. You may receive the same event more than once. The webhook-id header (the same as eventId in the body) is identical across all attempts; store it and drop duplicates.

webhook-id format
<orderId>:order.created
<orderId>:order.cancelled
<orderId>:order.updated:<status>
<orderId>:order.updated:delivery_failed     <- undeliverable, paid online (restaurant)
sentAt and webhook-timestamp change on every attempt. Do not use them for deduplication; use only webhook-id.

There is no ordering guarantee

Events may not arrive in the order they were produced. A retried event can arrive after one produced later; a delayed order.updated:preparing can arrive after ready.

Treat order.status in the body as authoritative and ignore any transition that moves backwards from your own record. When in doubt, reconcile with GET /orders/{id}. Exception: with deliveryFailed.completedSale: true, status: cancelled is not a cancellation but a sale paid online; it is not cancelled at the till.

Signature verification

There is a single signature scheme: Standard Webhooks. The header has the form webhook-signature: v1,<base64>; the signed content is {webhook-id}.{webhook-timestamp}.{raw body} and the algorithm is HMAC-SHA256. The part after the whsec_ prefix is base64-decoded and used as the raw-byte key; the official Standard Webhooks libraries verify the signature in this form. webhook-timestamp is part of the signature: the receiver should compare it with a tolerance window (recommended: 5 minutes) and compare the signature in constant time.

Manual verification
import crypto from "node:crypto";

function verify(rawBody, headers, secret) {
  const id  = headers["webhook-id"];
  const ts  = headers["webhook-timestamp"];
  const sig = headers["webhook-signature"];        // "v1,BASE64"
  if (!id || !ts || !sig) return false;

  // 1) Replay window: reject anything outside 5 min.
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;

  // 2) Signed content: {id}.{timestamp}.{rawBody}; use the RAW body.
  // Key: base64-decode what follows 'whsec_' (raw bytes).
  const key = Buffer.from(secret.slice("whsec_".length), "base64");
  const expected = "v1," + crypto
    .createHmac("sha256", key)
    .update(id + "." + ts + "." + rawBody)
    .digest("base64");

  // 3) Constant-time comparison.
  const a = Buffer.from(sig), b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Retries and permanent failure

Every response other than 2xx counts as a failure. Each attempt waits 10 seconds. What happens next depends on the code you return:

AlanTipAçıklama
5xx · 408 · 429 · timeout · ağ hatasıtransientA total of 8 attempts are made: an immediate attempt, then at intervals of 30 s, 1, 2, 4, 8, 16 and 32 min; about 63 minutes in total.
other 4xx (400, 401, 403, 404, 410, 422 ...)permanentThe same content would produce the same rejection, so it is not retried.
3xx redirect · unsafe addresspermanentA redirect response and an unsafe address are permanent failures too.

Events that are exhausted or permanently rejected are listed on the delivery-health card of the business console, on both verticals. If you have a transient problem, return 5xx; signal a permanent rejection with 4xx. Do not do long work synchronously: return 2xx first, then continue processing in your own queue.

Testing and the reliability model

POST /webhook/test verifies your endpoint without waiting for a live order: it sends a signed order.created to the registered address. The body is always a made-up sample order (isTest: true); no real order or customer data is sent. The webhook-id of the event has the form test-<uuid>; detect that and do not send it to the kitchen or to picking. The result is returned synchronously: even if the receiver rejects the event, the response is 200 and the result is in the body (ok, httpStatus, permanent, error). If no address is registered, 409 webhook_not_configured is returned.

Webhooks give low latency; the pull endpoints are the source of truth. If a webhook is missed, close the gap with GET /orders?updatedSince=…. Recommended: use webhooks for speed and polling for assurance.
PreviousReviewsNextMenu & item mapping
On this page
  • Events
  • Registering the address
  • Deduplication
  • There is no ordering guarantee
  • Signature verification
  • Retries and permanent failure
  • Testing and the reliability model