Görünüm
Başlangıç
En kısa yol: bir sipariş olayını imzalı olarak alıp doğrulamak.
Taban adres:
https://api.veni.com.trÖnkoşullar
- Aktif anlaşması olan bir entegratör kaydı. Abonelik açılırken
integrator_keyolarak gönderilir. Eksikse istek422integrator.required, anlaşma yoksa422integrator.not_approveddöner. Başvuru: veni.com.tr/iletisim. - HTTPS bir adres. Giden isteklerimiz özel ağ, loopback ve bulut metadata adreslerine bağlanmaz; adres internetten erişilebilir olmalı. Adres bir alan adı olmak zorundadır (çıplak IP kabul edilmez) ve entegratör kaydınızda bildirilen alan adlarından birine ait olmalıdır.
- Ham gövdeye erişim. İmza isteğin baytları üzerinden hesaplanır; gövdeyi yeniden serileştiren bir ara katman imzayı bozar.
1. Abonelik açılır
Aboneliği restoran kendi panelinden açar ve entegratör olarak sizi seçer. Aynı işlem restoran oturum anahtarıyla uçlardan da yapılabilir:
POST
/api/v1/restaurants/me/webhooksRestoran JWTAbone olunabilecek dört olay var; dördü de restoranın sipariş geçişleridir:
| Olay | Ne zaman gider |
|---|---|
order.accepted | Restoran siparişi kabul etti |
order.rejected | Restoran siparişi reddetti |
order.preparing | Hazırlık başladı |
order.ready | Sipariş kuryeye hazır |
bash
curl -X POST https://api.veni.com.tr/api/v1/restaurants/me/webhooks \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{
"url": "https://ornek.com/veni-webhook",
"events": ["order.preparing", "order.ready"],
"integrator_key": "pos.firmaniz"
}'Alan kuralları: url zorunlu, geçerli bir adres, en çok 2048 karakter. events zorunlu, en az 1 en çok 20 giriş; yukarıdaki dört addan biri olmalı — tanımadığımız bir ad reddedilir, böylece yazım hatası "kurulu görünen ama hiç tetiklenmeyen" bir abonelik üretmez. integrator_key zorunlu ve sonradan değiştirilemez; başka bir entegratöre geçmek için yeni abonelik açılır.
Yanıt 201. Bütün uçlar aynı zarfı kullanır: içerik data, istek kimliği meta.request_id.
json
{
"data": {
"id": "3a1f0c92-5b4e-4a77-9f21-8d6e1b40c7aa",
"integrator_key": "pos.firmaniz",
"url": "https://ornek.com/veni-webhook",
"secret_prefix": "whsec_9f",
"events": ["order.preparing", "order.ready"],
"is_active": true,
"created_at": "2026-10-12T09:14:07Z",
"secret": "whsec_… (YALNIZCA bu yanıtta)"
},
"meta": { "request_id": "01JB2K9X7Q" }
}2. Sır size ulaşır
secret alanı whsec_ ile başlar ve yalnızca oluşturma ile yenileme yanıtında döner. Aboneliği restoran açtıysa sırrı size o iletir. Sonraki listelemelerde yalnız ilk 8 karakteri (secret_prefix) görünür — hangi sırrın hangi uca ait olduğunu ayırt etmek için.
Sır kaybolursa geri okutma yolu yoktur; yenilenir:
POST
/api/v1/restaurants/me/webhooks/{id}/rotate-secretRestoran JWTYenilemede geçiş penceresi yok — sıra önemli
Bu uçla yenilenen sır anında yürürlüğe girer; eski sırla imzalanmış istek gelmez.
Kesinti istemiyorsanız sıra şu: doğrulayıcınız iki sırrı birden kabul edecek hâle gelsin → sır yenilenir → yeni sır yerleştirilir → eskisi kaldırılır. Yenilemeden sonra deneme ucuyla delivered: true geldiğini doğrulayın.
3. İmzayı doğrulayın
Gelen her isteği doğrulayın. Adresiniz internete açık olduğu için imza, isteğin gerçekten bizden geldiğinin tek kanıtıdır.
Tam kod ve sık yapılan hatalar: Webhook imzası.
Gövde şöyle gelir — dış zarf her olayda aynı, olaya özel kısım data içinde:
json
{
"id": "8f3c1e2a-77b0-4c19-9a52-0e3f7d14c6b1",
"event": "order.ready",
"occurred_at": "2026-10-12T09:14:07Z",
"data": {
"event": "order.ready",
"occurred_at": "2026-10-12T09:14:07Z",
"order": {
"id": "ord_9f3c",
"restaurant_id": "res_18",
"status": "ready",
"total_amount": 348.5,
"delivery_fee": 29.9,
"courier_type": "platform",
"items": [{ "name": "Tavuk dürüm", "quantity": 2 }],
"created_at": "2026-10-12T09:02:11Z"
}
}
}id teslimatın kimliğidir ve X-Veni-Delivery başlığıyla aynıdır. data.order sipariş nesnesinin müşteri verisi kırpılmış hâlidir; gösterilen alanlar bir kesittir ve gövdeye yeni alan eklenebilir. Tanımadığınız alanı yok sayın; sürüm değişmez.
Bu gövde müşteri verisi taşır
data.order sipariş notunu içerir; courier_type alanı own_courier ise teslimat adresini ve telefonu da içerir. Platform kuryesiyle giden siparişlerde adres boş, telefon maskelidir. Kural ve tablo: POS API — siparişler. Sisteminizde saklama süresi, erişim kısıtı ve silme sorumluluğu sizdedir.
4. Hızlıca 2xx dönün
İşiniz bittiyse gövdesiz bir 200 yeterlidir. Yanıtı beklemeden uzun iş yapmayın: olayı kendi kuyruğunuza atıp hemen dönmek en sağlıklısı.
Restoran webhook'ları gecikmeye duyarlı profille gider. Adresiniz cevap vermezse yeniden denemeler şöyle sıralanır:
| Deneme | Bekleme |
|---|---|
| 1 → 2 | 10 saniye |
| 2 → 3 | 30 saniye |
| 3 → 4 | 2 dakika |
| 4 → 5 | 10 dakika |
| 5 → 6 | 30 dakika |
Her beklemeye ±%20 rastgelelik eklenir. Bir teslimat böylece 6 deneme yapar; altıncı da tutmazsa teslimat tükenmiş sayılır ve ulaşamayanlar listesine düşer, oradan elle yeniden gönderilebilir. Takvim bilinçli olarak kısadır: iki saat sonra gelen "sipariş hazır" bildiriminin mutfağa faydası yoktur.
Üst üste 20 başarısız DENEMEde abonelik kapatılır ve gerekçesi damgalanır. ⚠️ Sayaç deneme başına artar, teslimat başına değil: bir teslimat 6 deneme yaptığı için art arda 4 ulaşılamayan olay aboneliği kapatmaya yeter (yaklaşık 3 saat). Tek bir başarılı teslimat sayacı sıfırlar.
5. Teslimatları izleyin
Restoran oturum anahtarıyla son 50 teslimat okunabilir:
GET
/api/v1/restaurants/me/webhooks/{id}/deliveriesRestoran JWTjson
{
"data": [
{
"id": "dlv_9",
"event_type": "order.ready",
"status": "delivered",
"attempt_count": 1,
"last_response_code": 200,
"delivered_at": "2026-10-12T09:14:08Z",
"created_at": "2026-10-12T09:14:07Z"
},
{
"id": "dlv_8",
"event_type": "order.preparing",
"status": "failed",
"attempt_count": 3,
"last_response_code": 502,
"last_error": "bad gateway",
"created_at": "2026-10-12T08:51:02Z"
}
],
"meta": { "request_id": "01JB2K9X7Q" }
}status beş değerden biridir:
| Değer | Anlamı |
|---|---|
pending | Kuyruğa alındı, henüz gönderilmedi |
delivering | Şu anda gönderiliyor |
delivered | 2xx aldık, iş bitti |
failed | Bu deneme tutmadı, yeniden denenecek |
exhausted | Deneme hakkı bitti; ulaşamayanlar listesinde |
Başarılı teslimat kayıtları 30 gün, ulaşamayanlar 180 gün saklanır.
Restoran oturum anahtarınız yoksa aynı bilgiyi firmanıza verilen süreli bağlantıdan okuyabilirsiniz: Teslimat günlüğü.
Aboneliği yönetmek
| Ne | Uç |
|---|---|
| Listeleme (+ desteklenen olaylar) | GET /api/v1/restaurants/me/webhooks |
| Adres, olay listesi ya da aktiflik değişikliği | PATCH /api/v1/restaurants/me/webhooks/{id} |
| Silme | DELETE /api/v1/restaurants/me/webhooks/{id} |
| Sır yenileme | POST /api/v1/restaurants/me/webhooks/{id}/rotate-secret |
| Teslimat geçmişi | GET /api/v1/restaurants/me/webhooks/{id}/deliveries |
Deneme (sentetik webhook.ping) | POST /api/v1/restaurants/me/webhooks/{id}/test |
Listeleme yanıtı data.items ve data.supported_events alanlarını taşır; ikincisi o anda abone olunabilecek olayların listesidir — kodunuza sabit yazmak yerine oradan okuyun.
Silmek aboneliği gerçekten kapatır: silinmiş bir abonelik olay almaya devam etmez.
Sırada ne var
- İmza ve sır yenileme — Webhook imzası
- Menü yazmak, sipariş okumak, durum güncellemek — POS API
- Kendi adınıza kurye çağırmak — Teslimatlar