API שלא צריך לשלם עליו פי שלוש.
ממשק REST מלא נפתח כבר במסלול השני. אצל רוב המתחרים בישראל הוא נפתח רק בשכבה השלישית.
יש גם אוסף Postman מוכן לייבוא.
POST /api/v1/shipments
Authorization: Bearer tk_live_...
Idempotency-Key: ord-1042
Content-Type: application/json
{
"external_id": "ORD-1042",
"recipient": {
"name": "רונית לוי",
"phone": "052-123-4567"
},
"delivery_address": {
"raw": "דיזנגוף 100, תל אביב",
"notes": "קומה 3"
},
"delivery_window": {
"from": "2026-08-21T09:00:00+03:00",
"to": "2026-08-21T13:00:00+03:00"
},
"service_date": "2026-08-21",
"items": [{ "description": "חבילה", "quantity": 1, "weight_kg": 3.5 }],
"cod": { "required": true, "amount_agorot": 14990 }
}
201 Created
{
"object": "shipment",
"id": "6a86673dc35a253754a924c1",
"reference": "TT-1042",
"external_id": "ORD-1042",
"status": "pending",
"livemode": true,
"tracking_url": "https://tiktak.apps4all.net/track/xK7qP2...",
"created_at": "2026-08-20T11:04:12.881Z"
}ארבעה דברים שכדאי לדעת לפני שמתחילים
אימות
כל בקשה נושאת Authorization: Bearer tk_live_… או tk_test_…. המפתח מוצג פעם אחת ביצירה; אצלנו נשמר רק תקציר שלו ואי אפשר לשחזר אותו. לכל מפתח יש הרשאות משלו, והוא לא יכול לקבל יותר ממה שיש למי שיצר אותו.
מצב בדיקה
מפתח tk_test_ כותב וקורא נתוני בדיקה בלבד. הם עוברים את אותה ולידציה ואת אותה מכונת מצבים כמו נתוני אמת — אבל לא נספרים במכסה, לא מחויבים, ולא מופיעים בלוח המשלוחים. מפתח live לא רואה אותם כלל, ולהפך.
Idempotency
שלחו Idempotency-Key בכל כתיבה. שליחה חוזרת עם אותו מפתח מחזירה את התשובה המקורית במקום לבצע שוב — זה מה שמונע משלוח כפול כשהחיבור נופל אחרי שהבקשה כבר הגיעה. אותו מפתח עם תוכן שונה נדחה ב-409. המפתח נשמר 24 שעות.
עימוד
עימוד מבוסס סמן ולא דילוג, כי משלוחים נוצרים בזמן שאתם עוברים על הרשימה ו-offset היה גורם לכפילויות ולדילוגים. כל רשימה מחזירה has_more ו-next_cursor. הסמן אטום — העבירו אותו כמו שהוא.
נקודות קצה
מסלולים ונהגים הם קריאה בלבד. מסלול הוא תוצאה של אופטימיזציה על כל היום — קיבולת רכב, חלונות זמן ומשמרות — ושילוב שיכתיב סדר עצירות ייצור תוכנית שהנהג לא יכול לעבוד לפיה. אתם יוצרים משלוחים; אנחנו מחליטים איך נוסעים אליהם.
- GET
/api/v1בדיקת מפתח והרשאות— - GET
/api/v1/shipmentsרשימת משלוחים עם סינוןshipments:read - POST
/api/v1/shipmentsיצירת משלוחshipments:write - GET
/api/v1/shipments/{id}משלוח בודדshipments:read - PATCH
/api/v1/shipments/{id}עדכון משלוחshipments:write - DELETE
/api/v1/shipments/{id}ביטול משלוחshipments:delete - GET
/api/v1/shipments/{id}/eventsציר הזמן של המשלוחshipments:read - GET
/api/v1/routesמסלולים לפי תאריךroutes:read - GET
/api/v1/routes/{id}מסלול בודדroutes:read - GET
/api/v1/driversרשימת נהגיםdrivers:read - GET
/api/v1/customersרשימת לקוחותcustomers:read - POST
/api/v1/customersיצירת לקוחcustomers:write - GET
/api/v1/webhooksכתובות webhookwebhooks:manage - POST
/api/v1/webhooksהוספת כתובתwebhooks:manage - PATCH
/api/v1/webhooks/{id}עדכון או רוטציית secretwebhooks:manage - DELETE
/api/v1/webhooks/{id}מחיקת כתובתwebhooks:manage - GET
/api/v1/webhooks/{id}/deliveriesיומן שליחותwebhooks:manage - POST
/api/v1/webhooks/{id}/testשליחת אירוע בדיקהwebhooks:manage
שגיאות
כל שגיאה חוזרת באותו מבנה. type לסיווג גס שעליו מסתעפים, code יציב למכונה, ו-request_id שכדאי לשמור בלוג — הוא מה שמאפשר לנו לאתר את הבקשה המדויקת שלכם.
- 400
invalid_request_errorמשהו בבקשה לא תקין - 401
authentication_errorמפתח חסר, שגוי או שפג תוקפו - 403
permission_errorלמפתח אין את ההרשאה הנדרשת - 404
not_found_errorלא נמצא — או שייך למצב אחר (live מול test) - 409
conflict_errorכפילות, או מצב שלא מאפשר את הפעולה - 429
rate_limit_errorחריגה מקצב הבקשות של המפתח - 500
api_errorתקלה אצלנו. שלחו לנו את ה-request_id
422 → לא. אצלנו שגיאת ולידציה היא 400.
400 Bad Request
{
"error": {
"type": "invalid_request_error",
"code": "validation_failed",
"message": "שדה recipient.name: Required",
"param": "recipient.name",
"doc_url": "https://tiktak.apps4all.net/developers#errors",
"request_id": "9c1e4a77b0d2f3e5"
}
}Webhooks
כל קריאה חתומה ב-HMAC-SHA256 מעל {t}.{body}, בכותרת TikTak-Signature. חותמת הזמן נכללת בחתימה בכוונה: בלעדיה אפשר היה לשדר מחדש קריאה שנלכדה, והיא הייתה מאומתת. אמתו תמיד — כתובת ה-webhook שלכם היא endpoint ציבורי, וכל מי שמכיר אותה יכול לשלוח אליה.
החזירו 2xx. כל תשובה אחרת גוררת ניסיון חוזר לפי 10 שניות, דקה, 5 דקות, 30 דקות, שעתיים, 6 שעות ו-24 שעות. הפניות (3xx) נחשבות כישלון — אנחנו לא מעבירים גוף חתום לכתובת שלא הסכמנו לדבר איתה. TikTak-Event-Id יציב בין ניסיונות, אז השתמשו בו לדה-דופליקציה.
// אימות חתימה — Node
import crypto from 'node:crypto';
export function verify(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(',').map((p) => p.trim().split('=')),
);
// מחוץ לחלון הזמן = ניסיון שידור חוזר של הודעה ישנה
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!(age < 300)) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
// השוואה בזמן קבוע, לא ===
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(parts.v1),
);
}<?php
// אימות חתימה — PHP
function tiktak_verify(string $body, string $header, string $secret): bool {
$parts = [];
foreach (explode(',', $header) as $piece) {
[$k, $v] = array_map('trim', explode('=', $piece, 2));
$parts[$k] = $v;
}
if (abs(time() - (int) $parts['t']) > 300) {
return false;
}
$expected = hash_hmac('sha256', $parts['t'] . '.' . $body, $secret);
return hash_equals($expected, $parts['v1']);
}אירועים
shipment.createdמשלוח חדש נוצרshipment.updatedפרטי משלוח עודכנוshipment.assignedהמשלוח שויך לנהגshipment.picked_upהמשלוח נאסףshipment.in_transitהנהג יצא לדרך עם המשלוחshipment.deliveredהמשלוח נמסרshipment.failedניסיון מסירה נכשלshipment.cancelledהמשלוח בוטלshipment.eta_updatedזמן ההגעה המשוער עודכןroute.publishedמסלול פורסם לנהגroute.startedהנהג התחיל מסלולroute.completedמסלול הושלםpod.capturedנקלטה הוכחת מסירהreview.submittedלקוח דירג את המסירה