Paket Oluştur — POST /plugin-api/packets/create ✓ Canlı

Eklentinin Restomenum'da yeni paket/delivery siparişi oluşturduğu yazma ucudur. Okuma uçlarından farklı: POST, JSON gövde alır ve orders:write scope'u ister. Sepet ürün id'leriyle gönderilir; fiyatlar Restomenum ürün kaydından alınır (gönderdiğiniz fiyat yok sayılır). idempotencyKey ile retry'da çift sipariş oluşmaz.

← API Uçları · ortak kurallar (base, auth, hata zarfı) orada.

Genel

Method / yolPOST /plugin-api/packets/create
Authinstall API key — Authorization: Bearer serverId.pluginId.secret
Scopeorders:write
Rate limitAyrı write kovası (varsayılan 20/dk; okuma kovasından sıkı) — Limitler
Content-Typeapplication/json

İstek

POST {RESTOMENUM_BASE}/plugin-api/packets/create
Authorization: Bearer <apiKey>     // install API key: serverId.pluginId.secret
Content-Type: application/json
  • Base: Sandbox https://sandbox.plugins.restomenum.app, Production https://plugins.restomenum.app (API Uçları).
  • Auth: Authorization: Bearer <apiKey> — token exchange'teki install API key.
İstek gövdesi
{
  "customer": {                      // zorunlu
    "id": "cust-123",                // zorunlu
    "name": "Ahmet Bayrak",          // zorunlu
    "address": "Örnek Mah. ...",     // zorunlu
    "region": "Kadıköy",             // ops
    "addressDescription": "3. kat",  // ops
    "phone": "5xx...",               // ops
    "call": "5xx..."                 // ops
  },
  "cart": [                          // zorunlu, 1..200 kalem
    {
      "product": "3b5d-f6d0",        // ürün id (products/list id'si) — zorunlu, ≤128
      "quantity": 2,                 // zorunlu (0.001..9999)
      "options": [                   // ops — ≤50 seçenek; ÜÇ biçim de geçerli, FİYAT BEYAN EDİLEMEZ
        { "id": "1693060234600" },   //   katalog kimliği — en kesin (aynı adlı seçenekte belirsizlik olmaz)
        { "title": "Ekstra Peynir" },
        "bal"                        //   düz string (eski biçim) — mevcut çağrılar çalışmaya devam eder
      ],
      "discount": 0,                 // ops (>= 0)
      "note": "az pişmiş",           // ops (≤500)
      "taxRate": 19,                 // ops (0..100) — satırın KDV oranı; ürünün oranını EZER ve dondurur
      "lineId": "kiosk-9f2c",        // ops — kararlı satır kimliği (1..64; kuver/new rezerve)
      "metadata": [ { "key": "course", "value": "main" } ]  // ops — DİZİ (map değil), yazar başına ≤10 öğe
    }
  ],
  "paymentNote": "nakit",            // zorunlu
  "payments": [                      // ops (<= 20). price >= 0; price > 0 olanlar saklanır
    { "id": "pm-1", "title": "Nakit", "price": 130, "isDiscount": false,
      "lineId": "kiosk-pay-1",       // ops — ödeme SATIRININ kimliği (id = YÖNTEM id'si!)
      "metadata": [ { "key": "terminal", "value": "kiosk-1" } ] }  // ops — DİZİ
  ],
  "note": "Kapıda zil çalma",        // ops (≤1000)
  "type": "takeaway",                // ops: 'dine_in' | 'takeaway' | 'delivery' (whitelist dışı → 400)
  "status": "Approved",              // ops: 'none' | 'Approved' (default 'Approved')
  "restaurantDelivery": true,        // ops (default true)
  "idempotencyKey": "order-9af2...", // ops AMA ÖNERİLİR (retry'da çift sipariş engeller)
  "callbackUrl": "https://..."       // ops (https, ≤500) — SAHİPLİK damgası + status hedefi; webhookUrl ile AYNI domain ŞART
}

Gövde alanları

AlanTipZorunluAçıklama
customerobjectevetid, name, address zorunlu; region, addressDescription, phone, call opsiyonel.
cart[]arrayevet1..200 kalem. Her kalem: product (ürün id, zorunlu, ≤128), quantity (0.001..9999, zorunlu), options (≤50 seçenek — { id } | { title } | düz string; fiyat beyan edilemez), discount (≥0), note (≤500), taxRate (0–100, aşağı bkz.).
cart[].taxRatenumberhayır0–100. Satırın KDV oranı. Verilirse ürünün oranını ezer ve satırda dondurulur; verilmezse ürünün oranı kullanılır. Geçersiz değer → 400 (Invalid taxRate (0-100 expected): <productId>) — sessizce ürüne düşmez. Beyanı yapan eklenti satırda taxRateBy ile kaydedilir — bu iç denetim izidir, hiçbir okuma yanıtında dönmez (teyitli). Gönderilmezse oran ürün kaydından okunur.
paymentNotestringevetÖdeme notu (ör. "nakit").
payments[]arrayhayır≤20. { id, title, price, isDiscount? }. price ≥ 0; yalnız price > 0 saklanır (price ≤ 0 satırlar düşer). paid sunucuda toplanır; paid > total → 400. Normal satır: id tenant'ın gerçek yöntemi olmalı (payment-methods/list'ten al); title/cash/noreport yöntem kaydından türetilir. isDiscount:true: doğrulamadan muaf (serbest id), title zorunlu. Aşağı bkz.
notestringhayırSipariş notu (ör. "Kapıda zil çalma") — ≤1000 karakter.
typeenumhayırSatışın tüketim biçimi — 'dine_in' | 'takeaway' | 'delivery'. Whitelist dışı değer → 400. Gönderilmezse okumada null ("bilinmiyor") döner — sessizce bir biçime düşürülmez. Sonradan packets/update ile düzeltilebilir. Ayrıntı: Satış tipi (type).
statusstringhayır'none' | 'Approved' — varsayılan 'Approved'.
restaurantDeliverybooleanhayırRestoranın kendi kuryesiyle teslim — varsayılan true.
idempotencyKeystringönerilirKendi benzersiz sipariş anahtarın — ≤128 karakter. Aynı anahtarla retry → aynı paket, çift sipariş yazmaz (24sa pencere).
callbackUrlstring (https)hayırSahiplik damgası + status-bildirim hedefi. Kurallar: https, ≤500 karakter, manifest webhookUrl'ünüzle aynı registered domain, private IP'ye çözünmemeli (ihlalde 400). Aşağı bkz.

Yanıt

// başarı
{
  "success": true,
  "data": {
    "packetId": "30e9642a-2c9c-4e3d-92b1-ec57b35c0e2e",
    "docNo": 1,
    "docNoDate": "2026-09-04",
    "businessDate": "2026-09-04"
  }
}

// hata
{ "success": false, "message": "<açıklama>" }

data.packetId oluşan paketin id'sidir → tam detayı packets/get?packetId= ile çekebilirsin.

Yukarıdaki gövde gerçek bir çağrıdan yakalanmıştır. docNo orada 1 çünkü o tenant'ın ilk paketiydi — gerçek bir işletmede dört-beş haneli olur. Tam sözleşme:

AlanTipZorunluAçıklama
packetIdstring✓Oluşan paketin id'si — UUID v4 ve paket dokümanının kimliğinin kendisi (bu yüzden pakette ayrı bir satış uuid'sine gerek yoktur; masada durum farklıdır). Sonraki packets/update-* çağrılarında bu değeri kullan. Alan adı packetId'dir, uuid DEĞİL — platformun iç modelinde uuid diye geçer ama dış sözleşmede packetId olarak yayınlanır.
docNonumber | null–Tenant başına atomik artan fiş numarası (adisyon/belge no). ⚠️ number, string DEĞİL — sayı olarak tiple.
docNoDatestring | null–Fiş numarasının hangi seriden/günden kesildiği — YYYY-MM-DD.
businessDatestring | null–Satışın hangi iş gününe ait olduğu — YYYY-MM-DD.
docNoDate ile businessDate AYNI ŞEY DEĞİL. Biri numaranın kaynağı (hangi seriden/günden kesildi), diğeri satışın ait olduğu iş günü. Çoğu gün aynı görünürler ama gün sınırında ve geri-açma senaryolarında ayrışırlar — örneğin gece yarısını geçen bir vardiyada businessDate bir önceki güne düşebilirken docNoDate numaranın yazıldığı günü gösterir. Karıştıran bir entegrasyon raporlamayı yanlış güne yazar. Aynı değeri gördüğün için tek alana indirgeme cazip gelir — indirgeme.
businessDate'i bu yanıttan SAKLA — geri okuyamazsın. Değer create yanıtında dolu gelir ama paket dokümanına yazılmaz (doküman düzeyindeki businessDate yalnız geri-açma pin'idir), dolayısıyla sonradan packets/get ile geri okunamaz. İhtiyacın varsa kendi kaydına yaz.
messageAnlam
joi doğrulama mesajıGeçersiz gövde (eksik/yanlış alan).
Product not found: <id>cart'taki bir ürün mağazada yok (TÜM ürünler var olmalı).
Invalid taxRate (0-100 expected): <id>Satırdaki taxRate aralık dışı/geçersiz (400). Sessizce ürünün oranına düşülmez.
Invalid lineId: <id>Biçim ihlali ya da rezerve kimlik (kuver/new) — 400. Satır kimliği.
Invalid option: <ürün>: <neden> (<beyan>)Seçenek ürünün katalogunda yok, başlıkla beyan farklı fiyatlı birden çok seçenekle eşleşiyor (belirsiz → { id } ile beyan et) ya da aynı seçenek tekrar beyan edilmiş (400).
Duplicate lineId: <id>Aynı istekteki listede kimlik tekrar ediyor (400).
lineId belongs to a cancelled lineİptal edilmiş (void) bir satırın kimliği yeniden beyan edildi (409).
metadata must be an array of {key, value}metadata düz map gönderildi — nihai şekil { key, value } dizisidir (400).
Invalid metadata: <ürün|payments[i]>: <sebep>Geçersiz key/değer, aynı yazarın tekrar eden anahtarı ya da yazar bütçesi (10 öğe / 256 karakter) aşımı (400).
Total metadata size after merge …İstek başına 20 KB ya da belge geneli 100 KB bütçe aşımı; mesaj (yours: X, other plugins: Y) kırılımını taşır (400).
unknown_payment_methodNormal ödeme satırının id'si tenant'ın tanımlı yöntemi değil (400). Önce payment-methods/list.
no_payment_methods_configuredTenant'ta hiç ödeme yöntemi tanımlı değil (400; id yanlışlığından ayrı kod).
Paid amount is greater than total…payments toplamı sipariş tutarını aşıyor.
Server not foundGeçersiz tenant.
Duplicate request already in progressAynı idempotencyKey ile eşzamanlı 2. istek.
plugin.scope.deniedorders:write onaylı değil.
callbackUrl must be under the same domain…callbackUrl manifest webhookUrl'ünüzle aynı registered domain'de değil.
callbackUrl rejected: <sebep>callbackUrl güvenlik kontrolünden geçemedi (private IP / DNS hatası vb.).
Hata sırası. Ürün bulunamadıysa oran kontrolüne bakılmaz — Product not found: <id> döner. Aynı istekte birden çok satırın oranı geçersizse hepsi tek mesajda listelenir. Ödeme doğrulaması da belge numarası (docNo) tüketiminden önce koşar — hatalı istek fiş numarası yakmaz.

Önemli kurallar

Fiyatlar OTORİTER. Satır fiyatı Restomenum ürün kaydından alınır; gövdede fiyat göndermezsiniz / gönderseniz de yok sayılır. Yalnız product (id) + quantity + options (beyan) + discount verirsiniz. (Güvenlik: client fiyat iddiası kabul edilmez.) payments[].price ödeme tutarıdır, ürün fiyatı değil.

Tek istisna oran: satır taxRate gönderebilir (0–100). Almanya'da oran ürüne değil siparişe bağlı olabilir (yerinde tüketim %19 / götürü %7) ve bu bilgi yalnız sipariş yüzeyinde doğar. Verilen oran satırda dondurulur: ürünün oranı sonradan değişse bile geçmiş satışın mali kaydı değişmez. Bu alan mali fişlemenin (fiscal.de) KDV kırılımının kaynağıdır.

Beyan satır bazındadır: aynı siparişte yemek indirimli orana çekilirken alkol %19'da kalabilir — paket seviyesinde tek bir bayrak bunu ifade edemezdi. Tenant başına kapatma yoktur; koruma katmanları 0–100 doğrulaması, satırda dondurma, taxRateBy izi ve orders:write scope'udur.
Ödeme yöntemi doğrulaması (önce payment-methods/list): normal ödeme satırlarının id'si tenant'ın gerçek bir yöntemi olmalı — keyfi id → unknown_payment_method. title yöntem kaydından türetilir (yok sayılır). İndirim satırları (isDiscount:true) muaftır ve title gerektirir.
  • Idempotency: idempotencyKey gönderin. Ağ retry / tekrar tesliminde aynı paketi döndürür, çift sipariş yazmaz (24sa pencere).
  • Tüm cart ürünleri var olmalı: biri bile eksikse istek reddedilir — yarım sipariş yazılmaz (Product not found).
  • options = seçenek beyanı — { id }, { title } ya da düz string. Fiyat beyan edilemez (şema reddeder); katalogtan okunur ve satır toplamına platformca dahil edilir. Okuma yanıtında seçenek daima { id, title, price } olarak döner.
  • Oluşan paket entegrasyon: "packet" ile yazılır; packet.created event'i abone eklentilere düşer.

cart[] — satır kimliği (lineId) & metadata

İki alan da opsiyoneldir; hiçbiri gönderilmezse davranış bugünküyle aynıdır (mevcut eklentiler etkilenmez). Tam kurallar: Satır Kimliği & metadata.

AlanTipZorunluAçıklama
cart[].lineIdstring–Kararlı satır kimliği — çağıran taşır, platform korur. 8–64 karakter (⏳ alt sınır 1 → 8'e çıktı), alfanümerik başlar, A-Z a-z 0-9 . _ : - içerir — ⚠️ noktalama yalnız İÇERİDE (pay_01H9ABCD geçer; _pay01H9ABCD ve -pay01H9ABCD 400); saf rakamdan oluşan kimlik reddedilir ("1", "42" → 400). kuver ve new rezervedir (büyük/küçük harf farketmez) → 400 Invalid lineId: kuver. Aynı istekteki liste içinde tekrar edemez → 400 Duplicate lineId: <id>; iptal edilmiş (void) bir satırın kimliğini yeniden beyan etmek → 409 lineId belongs to a cancelled line. Gönderilmeyen satıra platform üretir ve üretilen kimlikler beyan edilenlerle çakışmaz. ⚠️ Kimlik korunması koşulludur: her istekte yeniden beyan edilmelidir (aşağı bkz.).
cart[].metadataarray–Opak korelasyon alanı — { key, value } dizisi (düz map değil; map gönderen istek 400 metadata must be an array of {key, value} alır). Platform yorumlamaz: saklar ve okuma yanıtında yazar damgasıyla (by) döner. key alfanümerik başlar, A-Za-z0-9_-, ≤64; value string | number | boolean (iç içe obje / dizi / null → 400). Bütçeler: yazar başına ≤10 öğe / satır ve value ≤256 karakter, istek başına ≤20 KB (sepet ve ödeme ayrı), belge geneli birleşme sonrası ≤100 KB.
⏳ Grandfather — PLANLANAN davranış, bugün ETKİN DEĞİL. Af mantığı platformda yazılı ama şema katmanı isteği daha transaction'a girmeden reddettiği için ona ulaşılmıyor — dört yazma ucunun hepsinde ölçüldü. Bugün dokümanda zaten var olan zayıf bir kimlik de 400 alır. Ayrıntı: Satır kimliği → Grandfather. Aşağıdaki akış tasarlanan davranıştır:

Tam-sepet değiştirmede önceki yanıtta aldığın kimlikleri geri gönderirsin; kural konmadan önce yazılmış zayıf bir kimlik dokümanda duruyorsa onu reddetmek ilgisiz satırlar dahil tüm isteğini düşürür ve dokümanı kalıcı kilitlerdi. Bu yüzden:
1️⃣ Biçim geçersizse her zaman 400 — asla affedilmez.
2️⃣ Biçim geçerli ama zayıfsa (<8 karakter ya da saf rakam): kimlik dokümanda zaten varsa kabul edilir (grandfather), yoksa 400 (yeni zayıf beyan).
Kontrol transaction içinde, doküman zaten okunurken yapılır — ek okuma maliyeti yok.
⚠️ packets/create ve tables/create uçlarında grandfather YOKTUR: yeni satış açarlar, karşılaştırılacak mevcut doküman yoktur → her zayıf kimlik yeni beyandır ve 400 alır.
Öneri: anahtarını çifte çevir. Kural bir kalkan; yapısal çözüm (uuid, lineId) çiftini anahtar yapmaktır. uuid (kalıcı satış kimliği) artık her olayda geliyor ve satış bazında kesin ayrık — çift, bu hata sınıfını kuralın da ötesinde kapatır.
Altın kural: kimliği HER istekte yeniden beyan et. Bu uç tam değiştirme yapar — platform yalnız gelen listedeki kimlikleri dokümandakilerle eşler. Beyan edilmeyen satır "silinmiş" sayılır ve üzerindeki tüm metadata (başka eklentilerin yazdıkları dahil) onunla birlikte gider. İstek reddedilmez (meşru satır silme de bu yola düşer) ama platform kaybı loglar. Kimliği kendi kaydından türet ve kendi tarafında sakla — doğru/yanlış örneği.
metadata'ya PII yazmayın (ad, telefon, e-posta, adres, kart): sözleşme gereği yasaktır. Platform bu alanda desen taraması yapmaz — yanlış pozitif meşru bir mali kaydı reddedip satışı durdururdu; kural sözleşme düzeyindedir, ihlal eklentinin sorumluluğundadır. Müşteri verisi customers:read + rıza üzerinden alınır.
Görünürlük: alan tenant içi korelasyon verisidir (kiosk yazar, fiskal sağlayıcı okur) → her kanalda orders:read yeterlidir, müşteri-PII rızası gerekmez ve alan push gövdelerinden silinmez. note, paymentNote ve iptal reason bu kapsamda değildir — onlar gerçek kullanıcı girdisi taşır ve rıza kapısına tabi olmaya devam eder.

payments[] — satır kimliği (lineId) & metadata

İki alan da opsiyoneldir; hiçbiri gönderilmezse davranış bugünküyle aynıdır (mevcut eklentiler etkilenmez). Tam kurallar: Satır Kimliği & metadata.

AlanTipZorunluAçıklama
payments[].lineIdstring–Kararlı satır kimliği — çağıran taşır, platform korur. 8–64 karakter (⏳ alt sınır 1 → 8'e çıktı), alfanümerik başlar, A-Z a-z 0-9 . _ : - içerir — ⚠️ noktalama yalnız İÇERİDE (pay_01H9ABCD geçer; _pay01H9ABCD ve -pay01H9ABCD 400); saf rakamdan oluşan kimlik reddedilir ("1", "42" → 400). kuver ve new rezervedir (büyük/küçük harf farketmez) → 400 Invalid lineId: kuver. Aynı istekteki liste içinde tekrar edemez → 400 Duplicate lineId: <id>; iptal edilmiş (void) bir satırın kimliğini yeniden beyan etmek → 409 lineId belongs to a cancelled line. Gönderilmeyen satıra platform üretir ve üretilen kimlikler beyan edilenlerle çakışmaz. Göndermesen bile her ödeme satırı kalıcı bir lineId alır (platform üretir, pay- önekli — önceden ödeme kaydının kimliği hiç yoktu). ⚠️ Kimlik korunması koşulludur: her istekte yeniden beyan edilmelidir (aşağı bkz.).
payments[].metadataarray–Opak korelasyon alanı — { key, value } dizisi (düz map değil; map gönderen istek 400 metadata must be an array of {key, value} alır). Platform yorumlamaz: saklar ve okuma yanıtında yazar damgasıyla (by) döner. key alfanümerik başlar, A-Za-z0-9_-, ≤64; value string | number | boolean (iç içe obje / dizi / null → 400). Bütçeler: yazar başına ≤10 öğe / satır ve value ≤256 karakter, istek başına ≤20 KB (sepet ve ödeme ayrı), belge geneli birleşme sonrası ≤100 KB.
⏳ Grandfather — PLANLANAN davranış, bugün ETKİN DEĞİL. Af mantığı platformda yazılı ama şema katmanı isteği daha transaction'a girmeden reddettiği için ona ulaşılmıyor — dört yazma ucunun hepsinde ölçüldü. Bugün dokümanda zaten var olan zayıf bir kimlik de 400 alır. Ayrıntı: Satır kimliği → Grandfather. Aşağıdaki akış tasarlanan davranıştır:

Tam-sepet değiştirmede önceki yanıtta aldığın kimlikleri geri gönderirsin; kural konmadan önce yazılmış zayıf bir kimlik dokümanda duruyorsa onu reddetmek ilgisiz satırlar dahil tüm isteğini düşürür ve dokümanı kalıcı kilitlerdi. Bu yüzden:
1️⃣ Biçim geçersizse her zaman 400 — asla affedilmez.
2️⃣ Biçim geçerli ama zayıfsa (<8 karakter ya da saf rakam): kimlik dokümanda zaten varsa kabul edilir (grandfather), yoksa 400 (yeni zayıf beyan).
Kontrol transaction içinde, doküman zaten okunurken yapılır — ek okuma maliyeti yok.
⚠️ packets/create ve tables/create uçlarında grandfather YOKTUR: yeni satış açarlar, karşılaştırılacak mevcut doküman yoktur → her zayıf kimlik yeni beyandır ve 400 alır.
Öneri: anahtarını çifte çevir. Kural bir kalkan; yapısal çözüm (uuid, lineId) çiftini anahtar yapmaktır. uuid (kalıcı satış kimliği) artık her olayda geliyor ve satış bazında kesin ayrık — çift, bu hata sınıfını kuralın da ötesinde kapatır.
payments[].id satır kimliği DEĞİLDİR. O alan ödeme yönteminin kimliğidir (nakit/kart tanımı — bkz. payment-methods/list) ve yerli akışlarda satır uuid'si de taşıyabilir; polimorfiktir, satır silme her yerde onu hedefler. Bu yüzden kalıcı kimlik ayrı lineId alanına yazıldı; id'nin anlamı değişmedi. Okuma yanıtında ödeme satırı lineId (kimlik) ve methodId (yöntem) alanlarını ayrı ayrı döner.
Altın kural: kimliği HER istekte yeniden beyan et. Bu uç tam değiştirme yapar — platform yalnız gelen listedeki kimlikleri dokümandakilerle eşler. Beyan edilmeyen satır "silinmiş" sayılır ve üzerindeki tüm metadata (başka eklentilerin yazdıkları dahil) onunla birlikte gider. İstek reddedilmez (meşru satır silme de bu yola düşer) ama platform kaybı loglar. Kimliği kendi kaydından türet ve kendi tarafında sakla — doğru/yanlış örneği.
metadata'ya PII yazmayın (ad, telefon, e-posta, adres, kart): sözleşme gereği yasaktır. Platform bu alanda desen taraması yapmaz — yanlış pozitif meşru bir mali kaydı reddedip satışı durdururdu; kural sözleşme düzeyindedir, ihlal eklentinin sorumluluğundadır. Müşteri verisi customers:read + rıza üzerinden alınır.
Görünürlük: alan tenant içi korelasyon verisidir (kiosk yazar, fiskal sağlayıcı okur) → her kanalda orders:read yeterlidir, müşteri-PII rızası gerekmez ve alan push gövdelerinden silinmez. note, paymentNote ve iptal reason bu kapsamda değildir — onlar gerçek kullanıcı girdisi taşır ve rıza kapısına tabi olmaya devam eder.

Status (durum) yönetimi — ayrı mekanizma

Bu uç paketi oluşturur. Sonradan status'ü sürmek (Approved → OnDelivery → Delivered) için bu ucu DEĞİL, packets:status scope'u + status callback'lerini kullanın: packet.created event'inde size imzalı callbackUrl'ler (pickup / delivered / cancel) verilir; teslim/iptal durumlarını onları çağırarak setlersiniz (Getir/Yemeksepeti deseni — platform kendi siparişinin teslim durumunu sürer).

Gövdedeki callbackUrl = sahiplik damgası. Paketi sizin oluşturduğunuzu işaretler ve ileride statü bildirimlerinin hedefi olur. Sahiplik damgası packet.status.update gate'ini açar: restoran sizin oluşturduğunuz paketin statüsünü değiştirmeden önce size sorulur (allow/deny). (packet.close ise tenant-geneldir, sahiplik/callbackUrl gerektirmez.) Kurallar: https, ≤500 karakter, manifest webhookUrl'ünüzle aynı registered domain, private IP'ye çözünmemeli (ihlalde 400).
Bildirim gönderimi henüz aktif değil (yakında: packet.status_changed) —callbackUrl şimdilik saklanır + sahiplik/hook'u açar, ama statü değişiminde henüz çağrı yapılmaz. Status'ü siz setlemek istiyorsanız packets:status + statusCallback yolunu kullanın (yukarıda).

Örnek (curl)

curl -X POST https://plugins.restomenum.app/plugin-api/packets/create \
  -H "Authorization: Bearer <serverId>.<pluginId>.<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": { "id": "c1", "name": "Test", "address": "Adres" },
    "cart": [ { "product": "3b5d-f6d0", "quantity": 1 } ],
    "paymentNote": "nakit",
    "idempotencyKey": "order-9af2"
  }'