Entegrasyon
Webhook'lar
Bir sipariş ya da rezervasyon değiştikçe sunucunuza gelen imzalı bildirimler, imzanın doğrulanması, yeniden denemeler ve test.
Webhook, bir sipariş ya da rezervasyon değiştiğinde sunucunuza haber verir; sunucunuzun durmadan sormasına gerek kalmaz. Bu rehber webhook'un nasıl kurulduğunu, hangi olayların geldiğini, imzanın nasıl doğrulandığını ve teslimat kurallarını anlatıyor.
Webhook'u kurmak#
Webhook bir API anahtarına aittir. Mekanın sahibi adresi panelde, anahtarın üzerinde girer:
- API Anahtarları sayfasında anahtarın yanındaki Düzenle düğmesine basın. Yeni bir anahtar oluştururken de adresi girebilirsiniz.
- Webhook adresi alanına sunucunuzun olayları karşılayacak adresini yazın: herkese açık bir
https://adresi. - Kaydettiğinizde imza sırrı (
whsec_…) bir kez gösterilir. Kopyalayıp sunucunuzun yapılandırmasına girin.
- Sırrı Yenile yeni bir sır oluşturur; eski sır hemen geçersiz olur. Yeni sırrı sunucunuza hemen girin, yoksa gelen bildirimlerin imzası doğrulanamaz.
- Adresi silerseniz webhook ve imza sırrı birlikte kaldırılır. Adresi olmayan bir anahtar için hiçbir olay gönderilmez.
Sipariş olayları genellikle saniyeler içinde gelir. Cibusy boştayken bir dakikaya kadar sürebilir, çünkü o zaman olayı dakikada bir çalışan bir tarama gönderir. Rezervasyon olayları ise her zaman o taramayla, yaklaşık bir dakika içinde gelir: bir rezervasyon saniyeden saniyeye değil, biri karar verdiğinde değişir. Olayın anında gelmesine dayanan bir akış kurmayın.
Hangi siparişler takip edilir#
Bir anahtarın webhook'u, o anahtarın verdiği ya da bir tur eklediği siparişleri takip eder.
- Bir sipariş için ilk
order.updatedolayı sipariş verildiğinde, siparişin başladığı haliyle gönderilir. - Sipariş, verildiği andan itibaren 48 saat takip edilir. Bundan sonraki bir değişiklik bildirilmez;
GET /orders/{orderId}siparişin durumunu söylemeye devam eder. - Bir anahtara adres sonradan eklenirse, o anahtarın son 48 saatte verilmiş açık siparişlerinin güncellemeleri de gelmeye başlar.
- İptal edilen bir anahtarın siparişleri için olay gönderilmez.
Siparişin API'de görünen halindeki her değişiklik bir olaydır: durumu, ödeme durumu, masası, toplamı, ödenen tutar, kapanış zamanı, satırların eklenmesi, adetleri ya da durumları. Mekan tarafında düzeltilen bir ad ya da not bir değişiklik sayılmaz.
Hangi rezervasyonlar takip edilir#
Bir anahtarın webhook'u, o anahtarın aldığı rezervasyonları da takip eder; anahtarın Rezervasyon alabilir izni olması yeterlidir.
- Bir rezervasyon için ilk
reservation.updatedolayı rezervasyon alındıktan sonra, alındığı haliyle gönderilir. - Sonraki olaylar şunlarda gelir: mekan isteği kabul ettiğinde ya da reddettiğinde; rezervasyon misafir, mekan ya da yanıtsız kaldığı için iptal edildiğinde; misafir kapıda karşılandığında; ziyaret bittiğinde ya da kimse gelmediğinde.
- Rezervasyon, alındığı günden saatinin 48 saat sonrasına kadar takip edilir; reddedildiğinde, iptal edildiğinde ya da sonuçlandığında ve bu son hali teslim edildiğinde takip biter. Sonrası için
GET /reservations/{reservationId}durumu söylemeye devam eder. - Bir anahtara adres sonradan eklenirse, o anahtarın hala açık olan rezervasyonlarının güncellemeleri de gelmeye başlar.
- İptal edilen bir anahtarın rezervasyonları için olay gönderilmez.
Olay sayılan değişiklikler şunlardır: rezervasyonun durumu, saati, kişi sayısı ve kimin iptal ettiği. Mekanın düzelttiği bir not bir değişiklik sayılmaz.
Olaylar#
type | Ne zaman gelir |
|---|---|
order.updated | Anahtarın verdiği ya da tur eklediği bir sipariş değişti. data, GET /orders/{orderId} çağrısının döndürdüğü siparişin aynısıdır. |
reservation.updated | Anahtarın aldığı bir rezervasyon değişti. data, GET /reservations/{reservationId} çağrısının döndürdüğü rezervasyonun aynısıdır. |
webhook.test | POST /webhooks/test ile siz istediniz. data testin kime ait olduğunu söyler; hiçbir sipariş ya da rezervasyon değişmemiştir. |
Her olay, API'nin her yerdeki kurallarıyla yazılmış bir JSON gövdesidir (camelCase adlar, adıyla sabit değerler, UTC zamanlar). UTF-8 ile, boşluksuz ve bayt sırası işareti (BOM) olmadan gönderilir; burada okunsun diye girintili gösteriliyor:
{
"id": "evt_0f8e2c1a7d444b0e9d2a5c3e7b1f9a60",
"type": "order.updated",
"createdAt": "2026-10-01T09:42:17.481Z",
"data": { }
}id,evt_ve ardından 32 küçük harfli onaltılık karakterden oluşur. Birorder.updatedya dareservation.updatedolayınıniddeğeri, aynı siparişin ya da rezervasyonun aynı değişikliği tekrar teslim edildiğinde aynı kalır ve her yeni değişiklikte farklıdır. Yinelenen olayları bununla ayıklayın. Birwebhook.testolayınıniddeğeri her seferinde yenidir.createdAt, Cibusy'nin bu teslimatı hazırladığı andır.
order.updated olayının alanları#
idstringOlayın kimliği: evt_ ve 32 küçük harfli onaltılık karakter. Bir order.updated ya da reservation.updated olayı, aynı siparişin ya da rezervasyonun aynı değişikliği yeniden gönderildiğinde (başarısız bir denemeden sonra) hep aynı kimliği taşır; tekrarları bununla ayıklayın. Her yeni değişikliğin kendi kimliği vardır, sipariş daha önce bulunduğu bir duruma dönse bile. webhook.test olayının kimliği her seferinde yenidir. X-Cibusy-Event-Id başlığında da gönderilir.
typestringOlayın türü: order.updated, reservation.updated ya da webhook.test. X-Cibusy-Event-Type başlığında da gönderilir.
Değerler:order.updated
createdAtstring · date-timeBu gönderimin hazırlandığı an (UTC). Yeniden deneme yeniden hazırlanır, bu yüzden denemeler arasında farklı olabilir. Aynı siparişle ya da rezervasyonla ilgili olaylar sırasız gelebilir; hangisinin daha yeni olduğunu buna bakarak anlarsınız.
dataobjectBu gönderim hazırlandığında siparişin hali, GET /orders/{orderId} yanıtının biçiminde.
Sipariş alanları
idstring · uuidSiparişin kimliği. GET /orders/{orderId} ile kullanın.
numberintegerMekanın siparişi çağırdığı numara, mekan içinde 1'den başlayarak sayılır. Müşteriye gösterin; mutfak fişinde yazan numaradır. Yalnız mekan içinde benzersizdir.
venueIdstring · uuidSiparişin verildiği mekan.
typestringSiparişin misafire nasıl ulaştığı.
DineIn- Masada: tur, masanın açık hesabına eklenir ya da yeni bir hesap açar.
Takeaway- Gel-al: müşteri siparişi mekandan alır.
Delivery- Paket: mekan siparişi müşteriye götürür.
statusstringSiparişin geldiği aşama; sipariş satırlarından ve hesaptan her okumada yeniden hesaplanır.
Received- Mekan siparişi aldı, mutfak henüz başlamadı.
Preparing- Mutfak satırlardan en az birine başladı.
Ready- Her satır hazır ya da servis edildi.
Served- Her satır servis edildi.
OnTheWay- Paket mekandan çıktı.
Completed- Mekan hesabı kapattı.
Cancelled- Sipariş iptal edildi ya da üzerindeki her satır silindi.
paymentStatusstringHesabın ne kadarının mekanda ödendiği.
Unpaid- Mekanda henüz hiçbir şey ödenmedi.
PartiallyPaid- Hesabın bir kısmı ödendi.
Paid- Hesap ödendi.
tableobjectnull olabilirMasa siparişinin masası. Gel-al ve pakette null.
Sipariş masası alanları
idstring · uuidMasanın kimliği, GET /venues/{venueId}/tables listesindeki gibi.
namestringMasanın adı.
customerobjectnull olabilirGel-al ya da paket siparişin kimin için olduğu. Masa siparişinde ve müşteri bilgisi alınmamış siparişte null.
Müşteri bilgisi alanları
namestringnull olabilirMüşterinin adı.
phonestringnull olabilirMüşterinin telefon numarası, uluslararası biçimde.
addressstringnull olabilirPaketin gideceği adres.
paymentMethodstringnull olabilirMüşterinin söylediyse nasıl ödeyeceği. Aksi halde null.
Cash- Nakit.
Card- Kart.
MealCard- Yemek kartı.
notestringnull olabilirSiparişin verildiği mutfak notu. Not yoksa null.
linesobject[]Siparişteki her satır; başkalarının masa hesabına koyduğu satırlar ve sonradan iptal edilenler dahil. Zaten açık bir hesaba eklenen masa siparişinde hesabın tüm satırları buradadır; sipariş verme yanıtındaki addedLineIds hangilerini o çağrının yazdığını söyler.
Sipariş satırı alanları
idstring · uuidSatırın kimliği. Satır siparişte kaldığı sürece değişmez.
productIdstring · uuidÜrün, menüdeki gibi.
productNamestringÜrünün mekanın kendi sözleriyle adı.
portionIdstring · uuidnull olabilirSipariş edilen porsiyon, menüdeki gibi.
portionNamestringnull olabilirPorsiyonun sipariş verildiği andaki adı.
quantityintegerKaç adet.
unitPricenumberBir adedin fiyatı, ekstralar dahil, sipariş verildiği andaki haliyle.
totalnumberSatırın tuttuğu: unitPrice × quantity, kampanyalardan önce. İptal edilen ve mekanın ikram ettiği satırda 0.
statusstringSatırın mutfakta geldiği aşama.
Pending- Mutfakta sırada.
Preparing- Hazırlanıyor.
Ready- Hazır.
Served- Servis edildi.
Cancelled- İptal edildi. İptal edilmiş bir siparişin satırları da böyle okunur.
extrasobject[]Her adetteki ekstralar.
Satır ekstrası alanları
idstring · uuidEkstra, menüdeki gibi.
namestringEkstranın adı.
pricenumberSipariş verildiği anda adet başına tuttuğu.
removedIngredientsobject[]Her adetten çıkarılan malzemeler.
Çıkarılan malzeme alanları
idstring · uuidMalzeme, menüdeki gibi.
namestringMalzemenin adı.
notestringnull olabilirBu satır için mutfak notu. Not yoksa null.
orderedAtstring · date-timeSatırın siparişe konduğu an (UTC).
totalsobjectSiparişin tuttuğu ve ne kadarının ödendiği.
Tutarlar alanları
subtotalnumberSatırların ekstralarıyla menü fiyatı, kampanyalardan önce. İptal edilen satırlar sayılmaz.
discountnumberMekanın kampanyalarının ve indirimlerinin düştüğü tutar.
serviceFeenumberMekanın hizmet bedeli; kampanyalardan sonraki yiyecek tutarı üzerinden alınır. Mekan almıyorsa 0.
totalnumberSiparişin tuttuğu.
paidnumberMekanda bunun ne kadarının ödendiği.
remainingnumberHala borç olan tutar. Hesap ödendiğinde 0.
currencystringTüm tutarların para birimi, ISO 4217 kodu. Her zaman TRY.
createdAtstring · date-timeSiparişin verildiği an (UTC).
closedAtstring · date-timenull olabilirMekanın siparişi kapattığı an (UTC). Sipariş açıkken ve iptal edilmiş siparişte null.
reservation.updated olayının alanları#
idstringOlayın kimliği: evt_ ve 32 küçük harfli onaltılık karakter. Bir order.updated ya da reservation.updated olayı, aynı siparişin ya da rezervasyonun aynı değişikliği yeniden gönderildiğinde (başarısız bir denemeden sonra) hep aynı kimliği taşır; tekrarları bununla ayıklayın. Her yeni değişikliğin kendi kimliği vardır, sipariş daha önce bulunduğu bir duruma dönse bile. webhook.test olayının kimliği her seferinde yenidir. X-Cibusy-Event-Id başlığında da gönderilir.
typestringOlayın türü: order.updated, reservation.updated ya da webhook.test. X-Cibusy-Event-Type başlığında da gönderilir.
Değerler:reservation.updated
createdAtstring · date-timeBu gönderimin hazırlandığı an (UTC). Yeniden deneme yeniden hazırlanır, bu yüzden denemeler arasında farklı olabilir. Aynı siparişle ya da rezervasyonla ilgili olaylar sırasız gelebilir; hangisinin daha yeni olduğunu buna bakarak anlarsınız.
dataobjectBu gönderim hazırlandığında rezervasyonun hali, GET /reservations/{reservationId} yanıtının biçiminde.
Rezervasyon alanları
idstring · uuidRezervasyonun kimliği. GET /reservations/{reservationId} ile kullanın.
venueIdstring · uuidMasanın ayırtıldığı mekan.
statusstringRezervasyonun geldiği aşama; her okumada yeniden hesaplanır, bu yüzden liste, tek rezervasyon ve webhook olayı aynı rezervasyon için farklı şeyler söyleyemez.
Pending- Mekan henüz yanıtlamadı. Rezervasyonları alındığı anda onaylayan bir mekanda görülmez.
Confirmed- Mekan kabul etti ya da rezervasyonları alındığı anda onaylıyor. Masa bekleniyor.
Declined- Mekan isteği reddetti.
Cancelled- İptal edildi. Kimin iptal ettiğini `cancelledBy` söyler.
Seated- Misafir geldi ve kapıda karşılandı.
Completed- Ziyaret bitti.
NoShow- Mekan kabul etti ama kimse gelmedi.
startsAtstring · date-timeMasanın ne zaman için olduğu, UTC bir an olarak.
guestsintegerKaç kişi geleceği.
customerobjectMasanın kimin için olduğu, mekanda kayıtlı haliyle.
Rezervasyon sahibi alanları
namestringnull olabilirMisafirin adı.
phonestringnull olabilirMisafirin telefon numarası, uluslararası biçimde.
notestringnull olabilirRezervasyonun yapıldığı not. Not yoksa null.
codestringnull olabilirMekanın misafiri kapıda karşılarken kullandığı kod: misafirin söyleyebileceği ya da pageUrl sayfasındaki QR olarak gösterebileceği sekiz karakter. Kodu elinde tutan rezervasyonu görebilir ve iptal edebilir; yalnız misafire verin.
pageUrlstringnull olabilirRezervasyonun cibusy.com'daki kendi sayfası: rezervasyonu ve onaylandıktan sonra mekanın kapıda okuttuğu QR'ı gösterir. Bağlantısını verin ya da misafire gönderin. Herkese açık adresi olmayan mekanda null.
canCancelbooleanRezervasyonun API üzerinden hala iptal edilip edilemeyeceği: geçerli, misafir kapıda karşılanmamış ve saati gelmemiş.
checkedInAtstring · date-timenull olabilirMisafirin kapıda karşılandığı an (UTC). O zamana kadar null.
cancelledAtstring · date-timenull olabilirRezervasyonun iptal edildiği an (UTC). status değeri Cancelled değilse null.
cancelledBystringnull olabilirKimin iptal ettiği. status değeri Cancelled değilse null.
Customer- Misafir: bu API üzerinden, rezervasyonun kendi sayfasından ya da Cibusy uygulamasından.
Venue- Mekan.
System- Cibusy: mekan isteği, rezervasyonun saatine kadar yanıtlamadı.
cancellationReasonstringnull olabilirİptal nedeni olarak söylenen, söyleyenin kendi sözleriyle. status değeri Cancelled değilse null.
createdAtstring · date-timeRezervasyonun yapıldığı an (UTC).
webhook.test olayının alanları#
idstringOlayın kimliği: evt_ ve 32 küçük harfli onaltılık karakter. Bir order.updated ya da reservation.updated olayı, aynı siparişin ya da rezervasyonun aynı değişikliği yeniden gönderildiğinde (başarısız bir denemeden sonra) hep aynı kimliği taşır; tekrarları bununla ayıklayın. Her yeni değişikliğin kendi kimliği vardır, sipariş daha önce bulunduğu bir duruma dönse bile. webhook.test olayının kimliği her seferinde yenidir. X-Cibusy-Event-Id başlığında da gönderilir.
typestringOlayın türü: order.updated, reservation.updated ya da webhook.test. X-Cibusy-Event-Type başlığında da gönderilir.
Değerler:webhook.test
createdAtstring · date-timeBu gönderimin hazırlandığı an (UTC). Yeniden deneme yeniden hazırlanır, bu yüzden denemeler arasında farklı olabilir. Aynı siparişle ya da rezervasyonla ilgili olaylar sırasız gelebilir; hangisinin daha yeni olduğunu buna bakarak anlarsınız.
dataobjectTestin kime ait olduğu.
Test olayı verisi alanları
venueIdstring · uuidAPI anahtarının ait olduğu mekan.
keyPrefixstringTesti gönderen anahtarın ilk karakterleri, mekan panelindeki listede göründüğü gibi. Anahtarın kendisi değil.
messagestringBunun ne olduğunu söyleyen bir satır.
İsteğin başlıkları#
Cibusy adresinize şu başlıklarla bir POST isteği gönderir:
| Başlık | Değer |
|---|---|
Content-Type | application/json |
User-Agent | Cibusy-Webhooks/1.0 |
X-Cibusy-Event-Id | Olayın id değeri. |
X-Cibusy-Event-Type | Olayın türü: order.updated, reservation.updated ya da webhook.test. |
X-Cibusy-Signature | t=<unix saniyesi>,v1=<imza>: bu denemenin zamanı ve küçük harfli onaltılık imza. Her yeniden denemenin kendi t değeri ve kendi imzası vardır. |
İmzayı doğrulamak#
Her teslimatı işlemeden önce doğrulayın. İmza, gövdenin Cibusy'den geldiğini ve yolda değişmediğini kanıtlar.
- İsteğin ham gövdesini okuyun: alınan baytların kendisini, bir JSON çözümleyici dokunmadan önce. Önce doğrulayın, sonra çözümleyin; çözümlenmiş JSON'u yeniden yazmak aynı baytları vermez.
X-Cibusy-Signaturebaşlığını,işaretinden bölün vet(saniye cinsinden bir Unix zamanı) ilev1değerlerini okuyun.tsizin saatinizden 5 dakikadan fazla uzaksa isteği reddedin. Bu, eski bir teslimatın yeniden oynatılmasını önler. Her yeniden denemenin kenditdeğeri olduğundan yeniden denenen bir teslimat bu kontrolden geçer.t+.+ ham gövde mesajının HMAC-SHA256 değerini hesaplayın. Anahtar olarak imza sırrının tamamını, panelin gösterdiği gibiwhsec_önekiyle birlikte kullanın. Sırrın ve mesajın UTF-8 baytlarını alın ve sonucu küçük harfli onaltılık yazın.- Sonucu
v1ile sabit zamanlı bir karşılaştırmayla karşılaştırın.
<?php
$secret = getenv('CIBUSY_WEBHOOK_SECRET'); // whsec_...
$rawBody = file_get_contents('php://input'); // the exact bytes received
$header = $_SERVER['HTTP_X_CIBUSY_SIGNATURE'] ?? '';
$parts = [];
foreach (explode(',', $header) as $part) {
[$key, $value] = array_pad(explode('=', trim($part), 2), 2, '');
$parts[$key] = $value;
}
$timestamp = $parts['t'] ?? '';
$signature = $parts['v1'] ?? '';
$expected = hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
if (!ctype_digit($timestamp)
|| abs(time() - (int) $timestamp) > 300
|| !hash_equals($expected, $signature)) {
http_response_code(400);
exit;
}
$event = json_decode($rawBody, true);
// De-duplicate on $event['id'], then handle the event.
http_response_code(204);Test vektörü#
Kodunuzu bununla deneyin: Cibusy'nin yazdığı haliyle bir webhook.test olayı, bu iş için uydurulmuş bir sırla imzalanmış. t değeri 1 Ekim 2026 09:30:00 UTC'dir; bu yüzden testinizde saati o ana ayarlamazsanız ya da yalnız o kontrolü atlamazsanız 3. adım onu reddeder.
secret whsec_PVJ2W1xHqfX6u1b0Jg9eT3nR5s8dKzYaLmQwEoCtUiA
body {"id":"evt_3f2a9c4e8b1d4f6a9e0c7b5d2a1f8e34","type":"webhook.test","createdAt":"2026-10-01T09:30:00.000Z","data":{"venueId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","keyPrefix":"cbk_a1B2c3D4","message":"This is a test event from Cibusy. No order has changed."}}
header t=1790847000,v1=0d704c590d7d639a6658090e4fbb189e4e4e0e410319f007cd509ad2e67c36c9Teslimat ve yeniden denemeler#
- Hızlıca
2xxile yanıt verin.2xxbir durum kodu olayın teslim edildiği anlamına gelir. Başka her şey başarısızlık sayılır: başka bir durum kodu, bir yönlendirme (izlenmez), açılması 5 saniyeden uzun süren bir bağlantı, toplamda 10 saniyeden uzun süren bir yanıt ya da bir bağlantı hatası. İşi yanıt verdikten sonra yapın. - Yeniden denemeler. Başarısız bir teslimattan sonra Cibusy, aynı değişiklik için 9 denemeye kadar tekrar dener; denemeler arasında 30 saniye, sonra 1, 2, 4, 8, 16, 32 ve 64 dakika bekler: toplamda yaklaşık iki saat. Sonra o değişiklik için denemeyi bırakır ve sipariş ya da rezervasyon yeniden değişene kadar başka bir şey göndermez. Rezervasyon olayları da aynı takvimle yeniden denenir. Her deneme yeniden, kendi
tdeğeriyle imzalanır. - Yinelenenleri ayıklayın. Bir yeniden deneme ve nadiren zaten alınmış bir teslimat, aynı
iddeğerini tekrar getirir. İşlediğiniziddeğerlerini hatırlayın ve tekrar geleni yok sayın. - Geçmişi değil, durumu okuyun. Bir olay, siparişi ya da rezervasyonu o teslimat hazırlandığındaki haliyle taşır. Aynı siparişe ya da rezervasyona ait teslimatlar sırasız gelebilir:
createdAtdeğerini elinizdekiyle karşılaştırın, hangisinin yeni olduğundan emin değilseniz şimdiki haliniGET /orders/{orderId}ya daGET /reservations/{reservationId}ile okuyun. - Adres.
https://olmalı ve herkese açık bir ana makine adına ya da adrese gitmelidir. Özel, loopback, link-local ya da bulut metadata adreslerine giden adresler hem kaydederken hem de olay gönderilirken reddedilir; yönlendirmeler hiçbir zaman izlenmez. - Gönderen IP adresi. Cibusy olayların hangi adresten geleceğini yayımlamaz. Bir teslimatın gerçek olduğunu IP adresi değil, imza kanıtlar.
Test etmek#
POST /webhooks/test, anahtarın adresine gerçek bir olay gibi imzalanmış bir webhook.test olayı gönderir ve sunucunuzun ne yaptığını söyler. Anahtarın Sipariş gönderebilir ya da Rezervasyon alabilir izinlerinden biri olması yeterlidir:
curl -X POST "https://api.cibusy.com/public/v1/webhooks/test" \
-H "X-Api-Key: $CIBUSY_API_KEY"{ "delivered": true, "statusCode": 204, "error": null, "durationMs": 182 }- Sunucunuz olayı kabul etse de etmese de yanıt
200olur; hangisi olduğunudeliveredsöyler. statusCode, sunucunuzun yanıt verdiği durum kodudur; hiç yanıt vermediysenullolur.error, olay teslim edildiysenull, edilmediyse neyin ters gittiğini anlatan kısa bir cümledir.- Çağrı sunucunuzun yanıtını en fazla 10 saniye bekler. Test olayı yeniden denenmez.
- Webhook adresi olmayan bir anahtar
409PUBLIC_API_WEBHOOK_NOT_CONFIGUREDile yanıtlanır. - Test, anahtarın sipariş ve rezervasyonlarla paylaştığı dakikada yaklaşık 30 isteklik bütçeden sayılır.