חברת תיירות בינלאומית שמוכרת חבילות סקי פנתה אליי עם בעיה שנשמעת קטנה ומתגלה כיקרה מאוד: כל שיחת תמיכה התחילה באותן שלוש דקות מבוזבזות. הלקוח כותב "יש לי שאלה על הטיסה" — והסוכן צריך לזהות מי זה, לאתר את ההזמנה ב-CRM, להיכנס למערכת התפעולית, למצוא את הקבוצה הנכונה, ורק אז לענות.
תכפילו את זה באלפי טיקטים בחודש, בעונה שבה הכל דחוס לארבעה חודשים, ותקבלו את ה-AHT האמיתי שלכם.
הפתרון: Action Flow אחד בזנדסק שרץ ברגע יצירת הטיקט ומביא את כל ההקשר לתוך הטיקט עצמו — לפני שהסוכן פותח אותו.
במאמר הזה אפרק את הפלואו שלב-שלב: מה כל צעד עושה, למה הוא שם, ואיזו בעיה עסקית הוא פותר.
למה Action Flows ולא Triggers ו-Webhooks
לפני הארכיטקטורה, החלטה אחת שחשוב להבין. אפשר היה לבנות את זה עם Trigger + Webhook + middleware חיצוני (Make / Workato / Lambda). בחרנו ב-Zendesk Action Flows משתי סיבות:
- Orchestration עם state. Trigger הוא fire-and-forget: הוא שולח webhook ולא יודע מה חזר. Action Flow מריץ רצף צעדים, שומר outputs של כל צעד, ומאפשר branching על סמך התשובה — בלי middleware שמישהו צריך לתחזק.
- הכל נשאר בתוך Zendesk Admin Center. אין שרת, אין credentials בעוד מקום, וה-connections מנוהלות ב-Connections של זנדסק. זה Native-first — עיקרון שאני מחזיק בו חזק: לא בונים תשתית חיצונית למה שהפלטפורמה כבר יודעת לעשות.
שלב 1: ה-Trigger — מתי בכלל לרוץ
הפלואו מתחיל ב-Ticket created עם condition על Group ID ספציפי. לא כל טיקט בחשבון צריך לגרור 6 קריאות API.
זו נקודה שאני חוזר עליה בכל implementation: Action Flow בלי entry condition הדוק הוא חוב טכני. אתם משלמים עליו בקריאות API, ב-rate limits, וב-failed runs שמסתירים תקלות אמיתיות.
שלב 2: Identity Resolution — מי כתב לנו
זו השכבה שהכל תלוי בה, וזו גם השכבה שהכי הרבה פלואים נשברים בה.
הפלואו משתמש ב-native actions של זנדסק — Look up ticket ו-Look up user — כדי לשלוף subject, description, via channel, אימייל וטלפון של ה-requester. ואז Custom Code step אחד (Prepare identity) מנרמל הכל לפורמט אחד:
- אימייל ל-lowercase
- טלפון ל-E.164 (+447XXXXXXX) וגם לפורמט ספרות בלבד (44XXXXXXXX), כי CRM-ים שומרים מספרים בשתי הצורות
- חילוץ מספר ההזמנה מה-subject, מה-description או מ-custom field ייעודי, עם regex
- כשאין ערך — placeholder מפורש, לא מחרוזת ריקה
הבעיה העסקית שזה פותר: לקוח שכותב ב-WhatsApp Business channel הוא end-user בזנדסק בלי אימייל בכלל. ה-identity היחיד שלו הוא טלפון. פלואו שמחפש רק לפי email פשוט לא יזהה אף אחד מהערוץ הזה — ובבדיקה בנתונים אמיתיים, זה בדיוק מה שקרה: 100% מטיקטי ה-WhatsApp הסתיימו ב-"לקוח לא נמצא".
הכלל שאני עובד לפיו: identity resolution היא שכבה עם סדר עדיפויות, לא שדה בודד. אימייל, טלפון, מספר הזמנה — כולם נבדקים, והתוצאה מנורמלת לפורמט אחד שכל שאר הפלואו עובד מולו.
שלב 3: מציאת הלקוח וההזמנה ב-CRM
Custom Action אחד מחפש את ה-contact ב-HubSpot CRM לפי email או phone או mobile, בשתי צורות הכתיבה של המספר, ומחזיר עד 3 contacts.
למה 3 ולא 1? כי duplicate contact records הם לא edge case. בדגימה של 15 מספרי טלפון, ל-3 מהם היו שתי רשומות שונות ב-CRM לאותו אדם — אחת מהאתר, אחת מהצ'אט. פלואו שלוקח רק את הרשומה הראשונה מפספס את העסקה שיושבת על השנייה.
משם, Custom Action שני מחפש deals פתוחים המשויכים לכל ה-contacts יחד (operator IN), מסונן לפי ה-pipeline הנכון ולפי רשימת dealstage IDs שנחשבים "חיים" — כלומר עסקה שבאמת רלוונטית לתמיכה, לא ליד ישן משנה שעברה.
ואז Custom Code step מכריע איזו עסקה זו:
- אם מספר ההזמנה שהלקוח ציין בטיקט תואם לאחת מהעסקאות שלו — זו העסקה.
- אם יש עסקה פתוחה אחת בלבד — זו העסקה.
- אחרת — tag לסוכן, ולא ניחוש.
הסעיף השלישי הוא עיקרון עיצובי, לא פשרה: מערכת שמנחשת הזמנה שגויה גרועה בהרבה ממערכת שאומרת "לא הצלחתי". בתיירות זה אומר להציג לסוכן תאריכי טיסה של לקוח אחר.
שלב 4: Journey Stage — לתרגם שלב מכירתי לשפת תמיכה
Custom Code step ממפה את ה-dealstage ID מה-CRM לשלב במסע הלקוח:
journey_pre_booking / journey_active / journey_on_trip / journey_post_trip
והוא מחזיר גם דגל שני: האם בכלל שווה לשלוף את פרטי הטיול. ללקוח לפני הזמנה אין מה לשלוף — והפלואו עוצר שם במקום לבזבז שתי קריאות API.
מה זה פותר: זה השדה שמניע אחר כך routing, macros, SLA שונה ללקוח on-trip (שהוא בהגדרה דחוף יותר), ודוחות Explore שמראים איפה נוצר העומס. הוא נכתב ל-ticket custom field — לא ל-tag.
שלב 5: הקבוצה הנכונה בתוך ההזמנה
כאן נכנסת מורכבות שספציפית לתיירות, ויש לה מקבילה כמעט בכל תעשייה: עסקה אחת ≠ יחידה תפעולית אחת.
הזמנה אחת יכולה להכיל כמה קבוצות נוסעים — משפחות שונות, מלונות שונים, אפילו תאריכים שונים. Custom Action מחזיר את כל הקבוצות עם שם ראש הקבוצה, שם המלון, מספר הנוסעים וסטטוס ההזמנה, והפלואו בודק: יש בדיוק אחת? ממשיכים. יש כמה? tag, והסוכן מכריע.
שלב 6: העשרת הטיקט — הדאטה שהסוכן באמת צריך
שני Custom Actions אחרונים מביאים את כל התמונה:
- פרטי הטיול: תאריכי יציאה וחזרה, אתר הסקי, שם המלון, מספר חדרים, שעות check-in/check-out, הוראות הגעה, קוד כספת, טלפון ישיר למלון, פרטי טיסות הלוך ושוב כולל booking reference ו-check-in link
- לוגיסטיקה: transfers (ספק, שעת איסוף, טלפון חירום, voucher), ski pass, השכרת ציוד, שיעורים, וסטטוס ההזמנות מול הספקים
ואז: Timestamp step שכותב מתי הדאטה נשלף, ו-Update ticket שכותב לשדות: מזהה עסקה, journey stage, מזהה קבוצה, תאריכי טיול, ו-Trip data last refreshed.
השדה האחרון קריטי ולרוב נשכח: דאטה מועשרת בלי חותמת זמן היא דאטה שאי אפשר לסמוך עליה. סוכן שרואה תאריך רענון מלפני חודש יודע לוודא מחדש.
שלב 7: כשלונות — לא שקטים, אלא מסומנים
לכל תרחיש שבו הפלואו לא הצליח להגיע לתשובה חד-משמעית יש tag משלו:
flow_no_contact (לא זוהה לקוח), flow_multi_deal (כמה הזמנות פתוחות), flow_multi_group (כמה קבוצות), flow_no_live_deal (אין הזמנה פעילה).
ה-tags האלה עושות שלושה דברים: הסוכן מבין בשתי שניות למה הטיקט ריק, אפשר לבנות views ו-Explore reports שסופרים בדיוק איפה הזיהוי נכשל, ואפשר להחליט לפי נתונים מה לתקן הלאה. זה ההבדל בין אוטומציה שמשתפרת לאוטומציה ששוקעת.
חלוקת עבודה שאני ממליץ עליה תמיד: custom fields לדאטה מובנית שמדווחים עליה, tags לסימון מצב תפעולי. לא להפך.
מה הפלואו הזה באמת מאפשר
ההעשרה עצמה היא לא המטרה — היא התשתית:
- Agent Copilot מייצר תשובות עם פרטי הטיול האמיתיים, לא גנריות
- AI Agents יכולים לענות לבד על "מתי הטיסה שלי" ו"איפה אוספים את הציוד" — deflection אמיתי, לא דחייה
- Routing ו-SLA לפי journey stage: לקוח on-trip הוא לא לקוח pre-booking
- Explore מקבל דאטה מובנית לפי אתר, שלב וסוג פנייה — במקום ניתוח טקסט חופשי
רוצים לבנות Action Flow כזה בזנדסק שלכם? דברו איתי ←