API ציבורי של Shipnest
REST API לפי scopes, פורמט תשובה אחיד, ו-Outbound Webhooks חתומים ב-HMAC. כל הדוגמאות כאן הן curl — אפשר להשתמש בכל שפה / כלי שמדבר HTTP.
Updated: 17 ביוני 2026
ה-Base URL של ה-API שלכם הוא הדומיין של פורטל הלקוחות שלכם (אותו דומיין שעליו רץ ה-Tenant). למשל:
https://app.shipnest.exampleאת ה-Base URL המדויק שלכם תמצאו בכרטיס הגדרות → API ו-Webhooks בפורטל. כל הנתיבים כאן יחסיים ל-Base URL הזה.
זמנים, מטבע, ושיטת הקידוד
Z בסוף (UTC), גם אם המערכת מציגה ללקוח בשעון ישראל. סכומי כסף ב-API נשלחים בשקלים (decimal) — המערכת ממירה אגורות (integer) פנימית, כך שלא צריך לדאוג לכך מצדכם.כל בקשה ל-/api/v1/* חייבת לכלול שני headers:
Authorization: Bearer ship_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-Timestamp: 1714838400- Authorization — מפתח API בפורמט
ship_live_…. מפתחות נוצרים ומבוטלים ב-self-service מתוך הפורטל, באותו דפוס של Stripe / GitHub / Vercel. - X-Timestamp — Unix time (שניות או מילישניות) של רגע הבקשה. הסטייה המותרת מול שעון השרת היא ±5 דקות. ההגבלה מצמצמת את חלון התקיפות מסוג Replay.
מחזור חיים של טוקן#
- יצירה ב-/portal/settings/api — בוחרים שם, רשימת scopes, ותאריך תפוגה אופציונלי (ללא תפוגה / 30 / 90 / 365 ימים).
- הטוקן הגולמי מוצג פעם אחת בלבד בדיאלוג אחרי היצירה. שומרים אותו מיד ב-secret manager (1Password, AWS Secrets Manager, Vercel env, וכו'). המערכת שומרת רק SHA-256 hash — לא נוכל לשחזר.
- אם הטוקן אבד או דלף — מבטלים אותו בקליק ויוצרים חדש. הביטול תקף מיידית. כל בקשה עם טוקן מבוטל מקבלת
401 Unauthorized.
הטוקן מוצג פעם אחת בלבד
מגבלות חשבון#
- עד 10 מפתחות פעילים בו-זמנית לכל Tenant.
- מפתחות שפג תוקפם או בוטלו לא נמחקים — הם נשמרים ל-audit (מי יצר, מתי, מי ביטל), אבל לא נספרים במכסה.
- פעולות יצירה/ביטול נכתבות ב-audit log של ה-Tenant ומופיעות בכרטיס "המפתחות שלי" עם זמני שימוש אחרון.
לכל מפתח מצורפת רשימה סגורה של scopes — מינימום ההרשאות שהוא צריך. בקשה ל-endpoint שדורש scope שאינו במפתח נדחית ב-403 Forbidden.
shipments:read | קריאת משלוחים, פרטי משלוח בודד, היסטוריית visits. |
shipments:write | יצירה ועדכון של משלוחים, וביטול דרך POST /shipments/[barcode]/cancel. |
customers:read | קריאת רשימת לקוחות ופרטי לקוח בודד. |
customers:write | יצירה ועדכון של לקוחות. |
webhooks:manage | ניהול subscriptions של webhooks — יצירה, עדכון, מחיקה, ושליחת test event. |
עקרון הרשאה מינימלית
shipments:read. כך, אם המפתח דלף, ההיקף הנזק מוגבל.Response Envelope#
כל תשובה — הצלחה או כישלון — עוטפת את התוכן באובייקט אחיד:
// הצלחה
{
"success": true,
"data": { ... },
"meta": { "count": 25, "nextCursor": "shp_5f3a...", "hasMore": true }
}
// שגיאה
{
"success": false,
"error": "Missing required scope: shipments:write"
}dataמכיל את התוכן — אובייקט בודד, או מערך בעמודי list. בקריאה ל-endpoint יחיד, יהיה אובייקט; בקריאה ל-list, יהיה מערך.metaמופיע רק בקריאות list ומכיל אתcount(מספר הפריטים בעמוד הנוכחי),nextCursor(cursor לעמוד הבא) ו-hasMore.hasMore: falseאוnextCursorריק = הגעתם לסוף.errorבקבועי שגיאה תמיד מחרוזת אחת מובנת. אין רמות (warning / info) — מה שלאsuccess: trueזה כישלון.
Rate limits ושגיאות#
Rate limiting#
כל endpoint מוגבל ל-60 בקשות לדקה לכל מפתח, לכל נתיב. כשהמכסה מוצתה — 429 Too Many Requests עם headers שמסבירים מתי לנסות שוב:
HTTP/1.1 429 Too Many Requests
Retry-After: 17
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1714838460Retry-After— מספר שניות עד שהחלון מתאפס. כבדו אותו, אל תעשו spam.- צריכים קצב גבוה יותר? פנו אלינו ונגדיל מכסה ספציפית למפתח.
קודי HTTP#
200 / 201 | הצלחה. 201 נשלח אחרי POST שיצר משאב חדש. |
400 | קלט לא תקין — ולידציה נכשלה. השדה הספציפי בדרך כלל מצוין ב-error. |
401 | חסר / לא תקף Bearer token, או X-Timestamp מחוץ לחלון הסטייה. |
403 | המפתח קיים ותקף, אבל לא כולל את ה-scope הנדרש ל-endpoint. |
404 | המשאב לא נמצא ב-Tenant הזה. שימו לב — לא חושפים אם המשאב קיים ב-Tenant אחר. |
409 | קונפליקט — למשל ניסיון ליצור משלוח עם barcode שכבר קיים. |
429 | חריגה ממכסת הבקשות. ראו Retry-After. |
500 | תקלת שרת אצלנו. אם זה חוזר — צרו קשר עם הפרטים מה-response. |
כל ה-endpoints נמצאים תחת /api/v1/. הכותרת של כל endpoint מציינת את ה-scope הנדרש.
משלוחים (Shipments)#
/api/v1/shipmentsshipments:readרשימת משלוחים, ממויינת מהחדש לישן. פגינציה דרך cursor.
limitnumber | מספר תוצאות. ברירת מחדל 50, מקסימום 100. |
cursorstring | id של המשלוח האחרון בעמוד הקודם (מתוך meta.nextCursor). |
statusstring | סינון לפי sendStatus: PENDING / SUCCESS / FAILURE. |
createdFromISO date | סינון תחתון לפי createdAt. |
createdToISO date | סינון עליון לפי createdAt. |
curl 'https://app.shipnest.example/api/v1/shipments?limit=20&status=SUCCESS' \
-H 'Authorization: Bearer ship_live_…' \
-H 'X-Timestamp: 1714838400'/api/v1/shipments/{barcode}shipments:readפרטי משלוח יחיד לפי ברקוד, כולל עד 20 ה-visits האחרונים שלו.
curl 'https://app.shipnest.example/api/v1/shipments/ABC-1001' \
-H 'Authorization: Bearer ship_live_…' \
-H 'X-Timestamp: 1714838400'/api/v1/shipmentsshipments:writeיצירת משלוח חדש במערכת המקומית בלבד — המשלוח לא יישלח אוטומטית ל-Lionwheel. עבור Tenants מסונכרנים מול Lionwheel, יש ליצור את המשלוח דרך API של Lionwheel וה-webhook יביא אותו אלינו.
barcodestring חובה | ייחודי גלובלית לכל Tenant. |
phonestring חובה | מספר טלפון של הנמען (פורמט בינלאומי או ישראלי). |
customerNamestring חובה | שם הנמען. |
sourceNamestring חובה | שם השולח (העסק שלכם). |
destinationCitystring חובה | עיר היעד. |
destinationStreetstring חובה | רחוב היעד. |
destinationNumberstring חובה | מספר בית. |
destinationFloor / destinationApartment / destinationNotes / destinationEmail | פרטי מסירה נוספים. destinationNotes נראית לשליח. |
sourceCity / sourceStreet / sourceNumber / sourcePhone | כתובת איסוף — אם משלוח לאיסוף ולא ממחסן ברירת המחדל. |
messagestring | ההודעה שתישלח אוטומטית ללקוח (אם הוגדרה תבנית). |
packagesnumber | מספר חבילות (ברירת מחדל 1). |
pricenumber | ערך הגוביינא בשקלים. 0 או חסר = ללא COD. |
weightnumber | משקל בק"ג. |
urgencystring | REGULAR / EXPRESS / URGENT. |
leaveNextToDoorboolean | להשאיר ליד הדלת אם הלקוח לא ענה. |
shipmentNote / orgNote | הערות פנימיות (לא נשלחות ללקוח). |
customerId / externalOrderId | קישור ללקוח קיים, או ID של ההזמנה במערכת המקור. |
regionCode | קוד אזור (לסיווג ידני / דוחות). |
ברירות מחדל בעת יצירה: sendStatus = "PENDING", sourceProvider = "api".
תגובה: 201 עם המשלוח שנוצר; 409 אם הברקוד כבר קיים ב-Tenant.
curl -X POST 'https://app.shipnest.example/api/v1/shipments' \
-H 'Authorization: Bearer ship_live_…' \
-H 'X-Timestamp: 1714838400' \
-H 'Content-Type: application/json' \
-d '{
"barcode": "ABC-1001",
"phone": "+972501234567",
"customerName": "ישראל ישראלי",
"sourceName": "החנות שלי",
"destinationCity": "תל אביב",
"destinationStreet": "אבן גבירול",
"destinationNumber": "10",
"packages": 2,
"price": 35,
"urgency": "REGULAR"
}'/api/v1/shipments/{barcode}shipments:writeעדכון שדות של משלוח קיים. שדות שלא נשלחו יישארו ללא שינוי. שדות פנימיים (id, tenantId, barcode, נתוני AI, ערכים שמגיעים מ-Lionwheel) אינם ניתנים לעדכון דרך ה-API.
phone / customerName / sourceName | פרטי שולח/נמען. |
destinationCity / destinationStreet / destinationNumber / destinationFloor / destinationApartment / destinationNotes / destinationEmail | כתובת ופרטי מסירה. |
sourceCity / sourceStreet / sourceNumber / sourcePhone | כתובת איסוף. |
message / packages / price / weight / urgency / leaveNextToDoor | פרטי המשלוח. |
shipmentNote / orgNote / customerId / externalOrderId | הערות וקישורים. |
sendStatus / errorMessage | סטטוס שליחה (יישומים שמנהלים שליחה מבחוץ). |
curl -X PATCH 'https://app.shipnest.example/api/v1/shipments/ABC-1001' \
-H 'Authorization: Bearer ship_live_…' \
-H 'X-Timestamp: 1714838400' \
-H 'Content-Type: application/json' \
-d '{ "destinationNotes": "פעמון 3", "urgency": "EXPRESS" }'/api/v1/shipments/{barcode}/cancelshipments:writeביטול משלוח. אצל משלוחים מסונכרנים מ-Lionwheel — הביטול נשלח גם לשם. הביטול מעדכן את הסטטוס ל-CANCELLED ופולט אירוע shipment.status_changed.
reasonstring | סיבת הביטול — נשמרת ב-audit ונשלחת ל-webhook. |
curl -X POST 'https://app.shipnest.example/api/v1/shipments/ABC-1001/cancel' \
-H 'Authorization: Bearer ship_live_…' \
-H 'X-Timestamp: 1714838400' \
-H 'Content-Type: application/json' \
-d '{ "reason": "הלקוח ביטל את ההזמנה" }'לקוחות (Customers)#
/api/v1/customerscustomers:readרשימת לקוחות, פגינציה לפי cursor.
limitnumber | ברירת מחדל 100, מקסימום 200. |
cursorstring | id של הלקוח האחרון בעמוד הקודם. |
isActiveboolean | סינון לפי סטטוס פעיל. |
searchstring | חיפוש חופשי לפי שם, טלפון או דוא"ל. |
/api/v1/customers/{id}customers:readפרטי לקוח יחיד. id הוא ה-id הפנימי של Shipnest (נוצר ביצירה).
/api/v1/customerscustomers:writeיצירת לקוח חדש.
namestring חובה | שם הלקוח (חברה / אדם). |
emailstring | דוא"ל לקשר. |
phonestring | טלפון לקשר. |
addressstring | כתובת חופשית. |
isActiveboolean | ברירת מחדל true. |
curl -X POST 'https://app.shipnest.example/api/v1/customers' \
-H 'Authorization: Bearer ship_live_…' \
-H 'X-Timestamp: 1714838400' \
-H 'Content-Type: application/json' \
-d '{ "name": "חברה לדוגמה בעמ", "phone": "03-1234567" }'/api/v1/customers/{id}customers:writeעדכון לקוח קיים. שדות שלא נשלחו יישארו ללא שינוי.
ניהול subscriptions של Webhooks#
ה-endpoints כאן מנהלים את ה-subscriptions של ה-webhooks — היכן Shipnest שולח אירועים. הם לא ה-webhooks עצמם (אלו נשלחים מ-Shipnest אליכם, ראו סעיף Webhooks).
/api/v1/webhookswebhooks:manageרשימת כל ה-subscriptions של ה-Tenant.
/api/v1/webhookswebhooks:manageיצירת subscription חדש.
urlstring חובה | ה-URL שאליו נשלח את ה-payloads. חייב להיות HTTPS בפרודקשן. |
eventsstring[] חובה | רשימת אירועים — ראו קטלוג האירועים. אפשר ["*"] לקבל הכל. |
descriptionstring | תיאור חופשי לזיהוי ב-UI. |
isActiveboolean | ברירת מחדל true. |
התגובה כוללת secret שנוצר ביצירה — שמרו אותו, הוא הסוד שמשמש לחתימת ה-HMAC. הוא מוצג פעם אחת בלבד.
/api/v1/webhooks/{id}webhooks:manageפרטי subscription יחיד (ללא ה-secret).
/api/v1/webhooks/{id}webhooks:manageעדכון של subscription — שינוי URL, רשימת events, או הפעלה/השבתה.
/api/v1/webhooks/{id}webhooks:manageמחיקת subscription. הפעולה idempotent — מחיקה של ID שכבר נמחק מחזירה 200.
/api/v1/webhooks/{id}/testwebhooks:manageשליחת אירוע test ל-subscription. שימושי כדי לוודא שה-URL שלכם מקבל בקשות, מאמת חתימות נכון, ומחזיר 2xx בזמן.
Outbound Webhooks#
Shipnest שולחת POST ל-URLs שהגדרתם כשמתרחש אירוע מתאים. ה-payload חתום ב-HMAC-SHA256 כדי שתוכלו לוודא שהבקשה הגיעה מאיתנו.
קטלוג האירועים#
shipment.created | משלוח חדש נכנס למערכת (UI / API / Lionwheel webhook). |
shipment.status_changed | סטטוס המשלוח השתנה. כולל ביטול דרך POST /shipments/[barcode]/cancel. |
shipment.assigned | משלוח שויך לשליח. ה-payload כולל action: "assigned" (שיוך ראשון) או "transferred" (העברה משליח לשליח). |
shipment.delivered | המשלוח נמסר (סטטוס COMPLETED או ROUNDTRIP_DELIVERED). נפלט גם מאפליקציית הנהג וגם מ-Lionwheel webhook. |
shipment.failed | שליח סימן את הביקור ככשל. ה-payload כולל reasonCode (no_answer / wrong_address / refused / closed / other) ו-reason טקסטואלי. |
visit.completed | ביקור הושלם (נמסר בהצלחה). מקביל ל-shipment.delivered אבל ברמת ה-Visit. |
cod.collected | גוביינא נגבתה לראשונה (מעבר מ-PENDING לסטטוס שאינו PENDING). |
customer.created | לקוח חדש נוצר (דרך API ציבורי או UI ניהול). |
Headers בכל delivery#
Content-Type: application/json
User-Agent: Shipnest-Webhooks/1.0
X-Webhook-Event: shipment.created
X-Webhook-Delivery-Id: a1b2c3d4e5f6...
X-Webhook-Signature: t=1714838400,v1=hex-digest...
X-Webhook-Timestamp: 1714838400מבנה ה-payload#
{
"event": "shipment.created",
"tenantId": "shipping-001",
"occurredAt": "2026-05-04T12:00:00.000Z",
"data": {
"shipmentId": 42,
"barcode": "ABC-1001",
"customerName": "חברה לדוגמה",
"phone": "+972501234567",
"destinationCity": "תל אביב",
"destinationStreet": "אבן גבירול",
"status": "UNASSIGNED"
}
}אימות חתימה#
ה-signature הוא בפורמט t=<unix>,v1=<hex>. לאימות, חשבו מחדש HMAC-SHA256 על המחרוזת `${t}.${rawBody}` והשוו ב-constant-time (כדי למנוע timing attacks).
import crypto from "crypto";
export function verify(rawBody: string, headerSig: string, secret: string): boolean {
const parts = Object.fromEntries(
headerSig.split(",").map((p) => p.split("="))
);
const t = parts.t;
const sent = Buffer.from(parts.v1, "hex");
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest();
if (sent.length !== expected.length) return false;
return crypto.timingSafeEqual(sent, expected);
}מאמתים על ה-raw body
ניסיונות חוזרים#
תגובה עם status ב-2xx נחשבת להצלחה. כל תגובה אחרת או timeout (10 שניות) נחשבים לכישלון ומפעילים backoff מעריכי:
- ניסיון 1 → 1 דקה
- ניסיון 2 → 5 דקות
- ניסיון 3 → 30 דקות
- ניסיון 4 → שעתיים
- ניסיון 5 → 12 שעות
לאחר 6 כישלונות סך הכל, ה-delivery יסומן exhausted ולא יישלח שוב. לוג המסירות זמין בלוח הבקרה של ה-Tenant.
Idempotency חובה
X-Webhook-Delivery-Id כ-key למניעת עיבוד כפול. לעולם אל תזהו אירוע ייחודי לפי occurredAt או תוכן ה-payload בלבד.ניהול secrets#
- שמרו את הטוקן (
ship_live_…) וה-webhook secret ב-secret manager. לעולם אל תכניסו ב-Git. - מפתח לכל סקריפט / שירות — לא לחלוק. אם דליפה תקרה, ביטול נקודתי לא יפיל את כל החיבורים שלכם.
- סבבו (rotate) טוקנים פעם בשנה לפחות. צרו חדש → בדקו → בטלו את הישן.
Polling או Webhooks?#
- Webhooks — עדיפים. תגובה בזמן אמת, ללא עומס מיותר. השקיעו ב-idempotency וב-queue אצלכם.
- Polling — רלוונטי רק לסנכרון חוזר של מצב מלא (למשל דוח יומי). אל תעשו polling כדי "לדעת אם משהו השתנה" — זה לא יעיל ולא יציב.
טיפול בשגיאות#
- 5xx ו-429 — תמיד retry עם exponential backoff (בערך כמו ה-backoff של ה-webhooks שלנו).
- 4xx (חוץ מ-429) — לא לחזור על אותה בקשה. אלו שגיאות לוגיות (ולידציה, scope חסר, משאב לא קיים). תקנו את הקלט.
- תיעדו כל בקשה שנכשלה ב-
X-Webhook-Delivery-Id/ response body שלנו — זה מאפשר תמיכה לסייע במהירות.
- /openapi.yaml — ספק OpenAPI 3.1 סטטי לייבוא ב-Postman / Insomnia / n8n / Make.
- /portal/settings/api — ניהול מפתחות, רשימת endpoints מותאמת ל-Base URL שלכם, ופרומפט AI מוכן ל-Claude / ChatGPT / Cursor.
- /docs/changelog — שינויים אחרונים, כולל הרחבות של קטלוג ה-events ושינויי scopes.