סקירה
האינטגרציה דו-כיוונית. eato שולחת Webhook חתום לכתובת שלכם בכל אירוע הזמנה, והמערכת שלכם מחזירה סטטוסים, יוצרת הזמנות טלפוניות ומעדכנת זמינות דרך ה-API. מתאים לקופות כמו Restigo, Tabit ו-ResApp, ולכל קופה שיודעת לקבל הזמנות ולהחזיר סטטוס.
כל הנתיבים יושבים תחת כתובת בסיס אחת:
https://eato.co.il/api/v1/merchant
- Restigo
- Tabit
- ResApp
- Make
- Zapier
- n8n
- Pipedream
- Slack
- Telegram
אין סביבת Sandbox ציבורית. אפשר לבקש חשבון בדיקה עם תפריט לדוגמה.
אימות
כל קריאה ל-/api/v1/merchant/* נושאת מפתח Bearer בכותרת Authorization. המפתח בנוי כך: eak_live_<12 hex>_<32 base62>. הוא מוצג פעם אחת בלבד בזמן היצירה, ונשמר אצלנו כגיבוב בלבד, כך שגם אנחנו לא יכולים לשחזר אותו.
יוצרים ומבטלים מפתחות בלוח הבקרה, תחת הגדרות ומפתחים. אפשר להחזיק כמה מפתחות פעילים במקביל, והביטול מיידי.
GET /api/v1/merchant/orders?status=preparing HTTP/1.1
Host: eato.co.il
Authorization: Bearer eak_live_a1b2c3d4e5f6_<32-char-secret>
Accept: application/jsonשרת לשרת בלבד. ה-API לא פתוח ל-CORS מדפדפן, ומפתח לא נכנס לאפליקציה או לקוד לקוח.
שגיאות
כל שגיאה חוזרת במבנה אחיד. השדה field מופיע רק בשגיאות אימות של הגוף.
{
"error": {
"code": "validation_error",
"message": "quantity must be at least 1",
"field": "lines[0].quantity"
}
}| HTTP | קוד | משמעות |
|---|---|---|
401 | unauthorized | מפתח חסר, שגוי או שבוטל |
403 | forbidden | למפתח אין הרשאה לפעולה, למשל החזר בלי הרשאת בעלים או מנהל |
404 | not_found | המשאב לא קיים |
409 | conflict | התנגשות עסקית, למשל החזר כפול או אותו Idempotency-Key עם גוף אחר |
409 | invalid_transition | מעבר סטטוס לא מותר |
422 | validation_error | הגוף לא עומד בסכמה. כולל field |
429 | rate_limited | חריגה מהמגבלה. כולל Retry-After |
500 | internal_error | תקלה בצד שלנו. אם זה חוזר, דווחו עם השעה והנתיב |
Webhooks
אנחנו שולחים POST עם גוף JSON לכתובת שרשמתם, ומצפים לתשובת 2xx תוך 8 שניות. כל תוצאה אחרת מפעילה ניסיון חוזר. רושמים כתובת בלוח הבקרה או דרך ה-API, ולכל כתובת יש כפתור בדיקה ששולח אירוע order.created חתום לדוגמה.
כותרות
| כותרת | תפקיד |
|---|---|
Content-Type | application/json |
X-Eato-Signature | חתימת HMAC במבנה t=<unix>,v1=<hex> |
X-Eato-Event | סוג האירוע, למשל order.created |
X-Eato-Delivery | מזהה המשלוח. קבוע לאורך כל הניסיונות החוזרים, ומשמש למניעת כפילויות |
User-Agent | eato-webhooks/1.0 |
מעטפת
{
"id": "7b1c2f6e-3d4a-4c1b-9f20-5a6b7c8d9e0f",
"event": "order.created",
"created_at": "2026-11-10T19:30:45.123Z",
"tenant_id": "napoli",
"data": {
"order_id": "9d2e4b1a-6c3f-4e8d-a1b2-c3d4e5f60718",
"number": 1247,
"status": "confirmed",
"total": 12700,
"method": "delivery",
"channel": "web",
"payment_method": "card",
"customer": { "name": "Dana", "phone": "+972541234567" },
"items": [
{
"item_id": "marg",
"name": "Margherita",
"quantity": 1,
"total": 5800,
"size": "family",
"modifiers": ["Right half: mushrooms"],
"note": null
}
],
"created_at": "2026-11-10T19:30:41.002Z"
}
}אירועים
| אירוע | מתי נשלח | שדות ב-data |
|---|---|---|
order.created | ההזמנה אושרה: מיד במזומן, אחרי אישור תשלום בכרטיס | order_id, number, total, method, items[] |
order.status_changed | כל מעבר סטטוס, חוץ מביטול והחזר | order_id, from, to, changed_at, courier_id |
order.cancelled | ההזמנה בוטלה | order_id, number, reason, cancelled_at |
order.refunded | ההזמנה סומנה כהחזר | order_id, number, amount, payment_method, reason |
order.ready_for_print | מתוכנן, עדיין לא נשלח | טרם הוגדר |
ניסיונות חוזרים
| ניסיון | מתי |
|---|---|
| 1 | מיד |
| 2 | דקה אחרי הכישלון |
| 3 | 5 דקות אחרי |
| 4 | 15 דקות אחרי |
| 5 | שעה אחרי |
| 6 | 6 שעות אחרי |
שישה ניסיונות לאורך כ-7.4 שעות. אחרי הכישלון השישי המשלוח מסומן abandoned. הוא נשאר בלוח הבקרה עם היסטוריה מלאה וכפתור Retry ידני.
אימות חתימה
הכותרת X-Eato-Signature בנויה כך: t=<unix>,v1=<hex>. הערך v1 הוא HMAC-SHA256 על t.raw_body עם הסוד של הכתובת. מאמתים מול הגוף הגולמי לפני פענוח ה-JSON, עם חלון של 5 דקות נגד שידור חוזר, ובהשוואה בזמן קבוע. חתימה לא תקינה מקבלת 401 ולא מעובדת.
const crypto = require("node:crypto");
function verifyEato(rawBody, header, secret) {
if (!header) return false;
const parts = Object.fromEntries(
header.split(",").map((p) => {
const i = p.indexOf("=");
return [p.slice(0, i).trim(), p.slice(i + 1).trim()];
})
);
const t = Number(parts.t);
if (!Number.isInteger(t) || !parts.v1) return false;
if (Math.abs(Date.now() / 1000 - t) > 300) return false; // 5 minute window
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`, "utf8")
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(parts.v1, "utf8");
if (a.length !== b.length) return false; // timingSafeEqual throws on different lengths
return crypto.timingSafeEqual(a, b);
}
// Express: keep the raw body, verify before JSON.parse
app.post("/eato/webhook", express.raw({ type: "application/json" }), (req, res) => {
const raw = req.body.toString("utf8");
if (!verifyEato(raw, req.get("X-Eato-Signature"), process.env.EATO_WEBHOOK_SECRET)) {
return res.status(401).end();
}
const delivery = req.get("X-Eato-Delivery"); // dedupe on this id
queue.push({ delivery, event: JSON.parse(raw) }); // process async
res.status(200).end(); // answer within 8 seconds
});Orders API
| Method | נתיב | מה עושה | פרמטרים |
|---|---|---|---|
GET | /orders | רשימת הזמנות | status, page, per_page (30, max 100) |
GET | /orders/{id} | הזמנה מלאה עם לקוח, פריטים, אפשרויות וסכומים | |
PATCH | /orders/{id}/status | עדכון סטטוס. ביטול מחייב reason | status, courier_id, reason |
POST | /orders/{id}/refund | סימון החזר (בעלים או מנהל) | reason (max 500) |
POST | /orders/manual | הזמנה טלפונית או בדלפק | customer_phone, method, payment_method, lines[] |
POST | /webhooks/endpoints | רישום כתובת Webhook | url, events[], active |
הזמנה ידנית (טלפון או דלפק)
טלפון בפורמט ישראלי מומר ל-E.164. בלי payment_method ההזמנה נרשמת כמזומן.
curl -X POST https://eato.co.il/api/v1/merchant/orders/manual \
-H "Authorization: Bearer $EATO_API_KEY" \
-H "Idempotency-Key: 2f1e7c1a-8b9d-4e2f-a3c4-d5e6f7a8b9c0" \
-H "Content-Type: application/json" \
-d '{
"customer_phone": "054-1234567",
"customer_name": "Dana",
"method": "pickup",
"payment_method": "cash",
"notes": "no onion",
"lines": [
{ "item_id": "marg", "quantity": 1, "size_id": "family", "option_ids": [] }
]
}'HTTP/1.1 201 Created
Content-Type: application/json
{
"order": {
"id": "5c7d9e1f-2a3b-4c5d-8e9f-0a1b2c3d4e5f",
"number": 1248,
"status": "pending",
"total": 5800,
"eta_at": "2026-11-10T19:52:00.000Z"
}
}החזר
בהחזר בכרטיס, הכסף עצמו מוחזר ידנית בממשק של ספק הסליקה. הקריאה רושמת את הכוונה ומחזירה money_action_required. החזר דורש מפתח של בעלים או מנהל.
{
"refund": {
"amount": 5800,
"reason": "customer cancelled",
"money_action_required": true,
"message": "Refund the card in the payment provider panel"
},
"money_action_required": true
}מעברי סטטוס
| מסטטוס | לסטטוס |
|---|---|
pending | confirmed, preparing, cancelled |
confirmed | preparing, cancelled |
preparing | in_oven, ready, cancelled |
in_oven | ready, cancelled |
ready | out_for_delivery, delivered, cancelled |
out_for_delivery | delivered, cancelled |
delivered | refunded |
cancelled | refunded |
מעבר לא חוקי מחזיר 409 invalid_transition. החזר עובר רק דרך POST /orders/{id}/refund. כל PATCH שולח גם Webhook לכל המנויים, כולל למי ששלח אותו, ולכן צריך מנגנון נגד לולאות.
Idempotency
שולחים כותרת Idempotency-Key עם UUID לכל פעולה לוגית. אותו מפתח עם אותו גוף מחזיר את התשובה המקורית עם X-Eato-Idempotent-Replay: true. אותו מפתח עם גוף אחר מחזיר 409 conflict. מפתחות פגים אחרי 24 שעות.
נתמך כרגע ב-POST /orders/manual. תמיכה בעדכון סטטוס ובהחזר מתוכננת.
מגבלת קריאות
600 קריאות בדקה לכל מפתח, בערך 10 בשנייה. חריגה מחזירה 429 עם Retry-After. מי שמתקרב למגבלה בדרך כלל נמצא בלולאה לא מסונכרנת, ועדיף לעבור ל-Webhooks במקום לשאול שוב ושוב.
| כותרת בתשובה | ערך |
|---|---|
X-RateLimit-Limit | 600 |
X-RateLimit-Remaining | כמה קריאות נשארו בדקה הנוכחית |
X-RateLimit-Reset | מתי המונה מתאפס |
Retry-After | בתשובת 429 בלבד: כמה שניות לחכות |
CORS, IP ותעבורה
HTTPS בלבד. כתובת Webhook בלי HTTPS, או כתובת פנימית, נדחית ברישום. ל-Webhooks היוצאים אין כתובת IP קבועה. מי שצריך רשימת IP מורשים פונה לתמיכה לקבלת פרוקסי ייעודי. עיבוד כבד כדאי להעביר לתור, כי המתנה לתשובה מוגבלת ל-8 שניות.
מוסכמות
| נושא | מוסכמה |
|---|---|
| כסף | מספר שלם באגורות. ₪10.50 = 1050. המטבע תמיד שקל ולא נשלח כשדה |
| זמן | ISO-8601 ב-UTC עם אלפיות שנייה |
| טלפון | E.164, למשל +972541234567. פורמט ישראלי מקומי מומר אוטומטית |
| טקסט | UTF-8, כולל עברית |
| זמינות | available_from ו-available_to בדקות מחצות (660 = 11:00), available_days מסכת 7 ביטים (127 = כל הימים) |
| מספר הזמנה | number הוא מספר רץ לכל מסעדה, מ-1001. המזהה הייחודי הוא order_id |
רשימת בדיקה לפני עלייה לאוויר
סומנו 0 מתוך 8.
שאלות ותמיכה
אין Sandbox ציבורי. לפי בקשה פותחים חשבון בדיקה עם תפריט לדוגמה.
אין עדיין תמיכה מובנית במדפסות דרך ה-API. כל שירות ביניים שמדבר HTTP יכול להירשם ל-order.created ולהדפיס. אירוע ייעודי order.ready_for_print מתוכנן.
לכל משלוח יש מזהה אחד, והוא נשמר בין הניסיונות החוזרים של אותו משלוח. לכן אפשר להשתמש בו למניעת כפילויות.
קוראים ל-refund כדי לסמן את ההזמנה, ומבצעים את ההחזר עצמו בממשק של ספק הסליקה.
לא כברירת מחדל. פנו לתמיכה לקבלת פרוקסי ייעודי.
נתקעתם? כתבו ל-[למילוי: מייל מפתחים] עם מזהה המשלוח מהכותרת X-Eato-Delivery, או דרך צור קשר.