Entegrasyon
Menüyü okumak
Kategoriler, porsiyonlar, ekstralar ve seçim grupları, kampanyalar, stok ve menünün bir kopyasını güncel tutmak.
Menü, sitenizin en sık okuyacağı şeydir. Bu rehber menünün yapısını, sipariş için gereken kimliklerin nereden geldiğini, kampanyaları ve stok durumunu ve menünün bir kopyasını sunucunuzda nasıl güncel tutacağınızı anlatıyor.
Menüyü almak#
GET /venues/{venueId}/menu mekanın kategorilerini ve içlerindeki ürünleri verir: her ürünün porsiyonları ve fiyatları, ekstraları, seçim grupları, çıkarılabilir malzemeleri, alerjenleri ve stok durumu.
curl "https://api.cibusy.com/public/v1/venues/3fa85f64-5717-4562-b3fc-2c963f66afa6/menu?lang=tr" \
-H "X-Api-Key: $CIBUSY_API_KEY"Menüde yalnız mekanın misafirlerine gösterdikleri yer alır. Gizlenmiş bir ürün ya da kategori hiçbir zaman listelenmez, bu yüzden sipariş de edilemez. Menü, mekanın QR menüsünün nasıl ayarlandığından etkilenmez. Fiyatlar TL cinsinden ve KDV dahildir.
Menünün yapısı#
data
└── categories[] kategoriler, mekanın gösterdiği sırayla
└── products[] ürünler → productId
├── portions[] porsiyonlar ve fiyatları → portionId
├── extras[] ekstralar → extraIds
├── optionGroups[] ekstralar üzerindeki seçim kuralları
└── removableIngredients[] çıkarılabilir malzemeler → removedIngredientIdsBir sipariş satırı bir ürünü (productId) bir porsiyonunda (portionId) adlandırır; isterse ekstralar ekler (extraIds) ve malzeme çıkarır (removedIngredientIds). Bu kimliklerin hepsi menüden gelir. Kategoriler ve ürünler zaten mekanın gösterdiği sırayla gelir; yeniden sıralamanız gerekmez.
Dil#
lang, mekanın dillerinden birinin iki harfli kodudur. Mekanın dilleri, mekanın bilgilerindeki languages listesindedir.
- Mekanın sunmadığı bir dil istenirse ya da
langhiç verilmezse menü mekanın varsayılan dilinde gelir. - Yanıttaki
language, metinlerin hangi dilde olduğunu söyler. Mekan hiç dil ayarlamadıysanullolur ve metinler mekanın yazdığı gibidir. langyalnız menünün dilini seçer. Hata mesajlarının diliniAccept-Languagebaşlığı seçer.
Porsiyonlar ve fiyatlar#
Ürünün fiyatı porsiyonundadır. Her porsiyonun bir price değeri vardır; isDefault, mekanın önceden seçtiği porsiyonu gösterir. Tek servisi olan ürünlerde genellikle "Standart" adlı bir porsiyon bulunur; o zaman ürünün adını tek başına gösterin.
weight ve calories porsiyonun üzerinde yazan gramaj ve enerjidir. Bunlar porsiyonun adının yanında gösterilecek bilgilerdir; fiyat bunlarla hesaplanmaz.
Kampanyalar#
Süren bir indirimin kapsadığı porsiyon, gösterilecek fiyatla birlikte bir campaign taşır:
{
"id": "b9f2a7d4-3e05-4c81-86b7-2d8e0f1a4b35",
"name": "Yarım porsiyon",
"price": 190,
"orderable": true,
"campaign": {
"campaignId": "0c2e4a6b-8d1f-4a3c-9e5b-7d9f1b3d5f70",
"name": "Öğle menüsü",
"badgeText": "%15",
"discountedPrice": 161.5,
"endsAt": "2026-10-01T12:00:00.000Z"
}
}price değerinin yanında discountedPrice gösterin; badgeText rozet için kısa bir etikettir, endsAt de bir geri sayım için kampanyanın bittiği an. Kampanya her çağrıda yeniden hesaplanır, bu yüzden bir happy hour tam zamanında başlar ve biter.
Burada yalnız porsiyonun kendisindeki düz indirimler görünür. Menü kampanyaları ve "iki al bir öde" gibi indirimler sepet fiyatlanırken uygulanır ve sepeti fiyatladığınızda discount alanında görünür.
Tartıyla satılan ürünler#
orderable: false olan porsiyon ölçüyle satılır. pricingUnit fiyatın hangi birim için olduğunu söyler, örneğin Kilogram, ve misafirin aldığı miktar mekanda tartılır. Böyle bir porsiyonu menünüzde gösterebilirsiniz ama API üzerinden sipariş edemezsiniz.
Ekstralar ve seçim grupları#
extras ürüne eklenebilecekleri fiyatlarıyla listeler. Bir ekstranın price değeri, birlikte sipariş edildiği porsiyonun fiyatına eklenir; ücretsiz bir ekstrada sıfırdır.
optionGroupId dolu olan ekstralar bir seçim grubunun seçenekleridir. Grup, o seçenekler üzerinde bir kural koyar:
| Alan | Kural |
|---|---|
selectionType: "Single" | Gruptan en fazla bir seçenek seçilebilir. |
selectionType: "Multiple" | Gruptan istenen sayıda seçenek seçilebilir. |
isRequired: true | Gruptan bir seçim yapılmadan ürün sipariş edilemez. |
Örnek menüdeki ızgara köftede "Pişirme" zorunlu ve tek seçimli bir gruptur: "Az pişmiş" ve "İyi pişmiş" seçeneklerinden tam olarak biri seçilmelidir. "Ekstra peynir" hiçbir gruba ait değildir ve serbestçe eklenir.
Seçilen ekstraların kimlikleri siparişte extraIds alanında gider ve satırın her adedi için geçerlidir. Ürünün kendi ekstrası olmayan, iki kez yazılan ya da bir grubun kuralını bozan bir seçim 400 PUBLIC_API_PRODUCT_OPTION_INVALID ile reddedilir.
Çıkarılabilir malzemeler#
removableIngredients, misafirin üründen çıkarılmasını isteyebileceği malzemelerdir; soğan gibi. Siparişte removedIngredientIds alanında gider ve satırın her adedi için geçerlidir.
Stok ve ürün bilgileri#
stockStatusürünün stokta (InStock), azalıyor (LowStock) ya da tükenmiş (OutOfStock) olduğunu söyler. Mekan ürünün stokunu takip etmiyorsanullolur; o zaman bir rozet göstermeyin. Tükenen ürün menüde kalır ama onun için verilen sipariş409PUBLIC_API_PRODUCT_UNAVAILABLEile reddedilir.allergensmekanın ürün için bildirdiği alerjenlerdir (AB yönetmeliğindeki 14 alerjen). Boş bir liste, ürünün alerjen içermediği anlamına gelmez; mekanın bir alerjen bildirmediği anlamına gelir.containsAlcoholvecontainsPorkDerivativesürünün alkol ya da domuz türevi içerip içermediğini söyler.trueolduğunda menünüzde bunu bir etiketle gösterin.acceptsMealCard, mekanın bu ürün için yemek kartı kabul edip etmediğidir.
Menünün bir kopyasını tutmak#
Menüyü sitenizin her ziyaretçisi için yeniden istemeyin. Bir kopyasını sunucunuzda tutun ve kullanmadan önce hala güncel olup olmadığını sorun:
- Menüyü okuduğunuzda yanıtın
ETagbaşlığını kopyayla birlikte saklayın. - Sonraki istekte bu değeri
If-None-Matchbaşlığında gönderin. - Menü değişmediyse yanıt gövdesiz bir
304 Not Modifiedolur ve kopyanızı kullanmaya devam edersiniz. Değiştiyse yeni menü yeni birETagile gelir.
const menus = new Map(); // "venueId:lang" → { etag, menu }
async function getMenu(venueId, lang) {
const key = `${venueId}:${lang}`;
const cached = menus.get(key);
const headers = { 'X-Api-Key': process.env.CIBUSY_API_KEY };
if (cached) headers['If-None-Match'] = cached.etag;
const response = await fetch(
`https://api.cibusy.com/public/v1/venues/${venueId}/menu?lang=${lang}`,
{ headers }
);
if (response.status === 304) return cached.menu;
const body = await response.json();
if (!body.success) throw new Error(`${body.errorCode}: ${body.developerMessage}`);
menus.set(key, { etag: response.headers.get('ETag'), menu: body.data });
return body.data;
}- Cibusy bir menüyü en fazla beş dakika önbellekte tutar ve mekan menüyü düzenlediğinde hemen yeniler.
ETagzarfın değil,dataalanının karşılığıdır: zarfıntimestampdeğeri her çağrıda değişse de menü aynıysaETagaynı kalır.- Yanıt
Cache-Control: private, no-cacheder: kopyayı saklayın, ama kullanmadan önce güncel olup olmadığını sorun.
Kopyanızı her ziyaretçide değil, belirli aralıklarla doğrulayın: örneğin en çok dakikada bir. Böylece hem hız sınırının çok altında kalırsınız hem de menüdeki bir değişiklik sitenize bir dakika içinde yansır.