דלג לתוכן הראשי

Users API

יצירה, עדכון וניהול פרופילי משתמשים בצורה תכנותית.

כל נקודות הקצה בעמוד זה יחסיות לכתובת הבסיס: https://api-eu1.joryio.com/api - ראו סקירת API.

אימות​

כל הבקשות דורשות אימות עם מפתח API:

Authorization: Bearer jry_live_your_api_key_here
Content-Type: application/json

מעקב (Track)​

דלת הקליטה האחת: מי המשתמש, מה הוא עשה ומה הוא קנה - בקריאה אחת.

כל שדה מקבל אובייקט בודד או מערך, כך שבקשה אחת יכולה לכסות אדם אחד או אלף.

נקודת קצה​

POST /users/track

גוף הבקשה​

שדהסוגחובהתיאור
attributesאובייקט או מערךלאשדות פרופיל. אלה נשמרים על האדם
eventsאובייקט או מערךלאדברים שקרו. מתווספים לציר הזמן
purchasesאובייקט או מערךלאהזמנות, באותו מבנה של POST /orders

נדרש לפחות אחד מהשלושה. מגבלות לבקשה: 1000 attributes, 500 events, 500 purchases. בקשה שחורגת נדחית ולא נחתכת - עיבוד שקט של 500 הראשונים מתוך 5000 היה מאבד את השאר בלי שום סימן.

attributes מיושמים ראשונים

בתוך בקשה אחת, attributes נכתבים לפני events ו-purchases, כך שאירוע יכול להתייחס לאדם שנוצר באותה קריאה.

זיהוי​

שדהתיאור
externalIdהמזהה שלכם, ומפתח היצירה-או-עדכון היחיד
userIdהמזהה הפנימי של Joryio (24 תווים הקסדצימליים). לעדכון בלבד - לעולם לא יוצר
anonymousIdסשן לפני זיהוי. אפשר לקשר אותו מאוחר יותר עם POST /users/identify
אין לשלוח Order Completed כאירוע

האירוע הזה נשלח אוטומטית כששולחים רכישה. שליחה ידנית שלו נדחית, כי שתי הדרכים שבהן זה משתבש שקטות: לבד הוא רושם הכנסה בלי הזמנה מאחוריה, כך שמסך ההזמנות, RFM וערך חיי לקוח לא רואים את המכירה; ויחד עם רכישה אמיתית הוא סופר את המכירה פעמיים.

התאמה לפי אימייל​

רכיב ב-attributes עם email אבל בלי externalId ובלי userId מותאם לפי האימייל (באותיות קטנות, בלי רווחים בקצוות), בתוך סביבת העבודה. כתובת אימייל אינה ייחודית - זוג או משפחה יכולים לחלוק כתובת אחת, ואדם אחד יכול להופיע תחת שני מזהים - ולכן:

  • אין איש קשר עם האימייל: נוצר איש קשר חדש.
  • בדיוק אחד: איש הקשר הזה מתעדכן.
  • כמה: מתעדכן בדיוק אחד - זה שהיה פעיל לאחרונה, איש הקשר שהפרופיל שלו השתנה לאחרונה (בתיקו, זה שנוצר אחרון). אותה בקשה בוחרת תמיד באותו איש קשר. האחרים נשארים כפי שהם: Joryio אף פעם לא ממזגת אנשי קשר ואף פעם לא מעדכנת את כולם.
  • ביטול הרשמה לרשימה ברכיב עדיין חל על כל אנשי הקשר עם האימייל הזה. הרשמה לרשימה חלה רק על איש הקשר שעודכן.

רכיב שהתאים לכמה אנשי קשר מופיע ב-attributes.matchedSeveral - הודעה, לא שגיאה: הכתיבה הצליחה.

{
"attributes": {
"processed": 1,
"failed": [],
"matchedSeveral": [
{ "index": 0, "userId": "6650f1c2a9e4b3d2c1f0e9a8", "count": 2 }
]
}
}

userId הוא איש הקשר שעודכן, ו-count הוא מספר אנשי הקשר שחולקים את הכתובת. matchedSeveral מופיע רק כשזה קרה. כדי לעדכן איש קשר מסוים במדויק, שלחו externalId (מותאם לפי externalId בלבד, גם כשהאימייל משותף) או את ה-userId של איש הקשר. ייבוא קבצים וסנכרוני מחסני נתונים מתאימים באותו אופן.

מנויים לרשימות​

איבר של attributes יכול גם להגדיר את המנויים של האדם לרשימות, כך שבקשה אחת יוצרת או מעדכנת את האדם, רושמת את האירועים שלו ומצרפת אותו לרשימה.

שדהסוגחובהתיאור
subscriptions[].listIdstringכןמזהה רשימת המנויים (Subscriptions API)
subscriptions[].channelstringלאemail (ברירת מחדל), sms, whatsapp, push או viber
subscriptions[].statusstringלאsubscribed (ברירת מחדל) או unsubscribed

עד 100 לכל איבר. הם מוחלים אחרי שהאדם נכתב, דרך אותו מסלול הסכמה של POST /subscriptions/contacts/:userId/lists/:listId, ונרשמים עם המקור api בהיסטוריית ההסכמה.

  • ביטול הרשמה קיים לעולם אינו מבוטל. אם האדם כבר ביטל את ההרשמה לרשימה הזו בערוץ הזה, בקשת subscribed מדולגת ומדווחת. רישום מחדש שלו הוא קריאה מכוונת לנקודת הקצה להרשמה. status: "unsubscribed" מוחל תמיד.
  • הרשימה חייבת להיות שייכת ל־workspace הזה. מזהה רשימה לא מוכר או בארכיון, או רשימה מ־workspace אחר, מדולג ומדווח ולעולם אינו נכתב.
  • אין מייל אישור. ה־API רושם את המנוי ישירות, גם ברשימה שדורשת אישור כפול (double opt-in) - שלב האישור הזה שייך לטפסי הרשמה. שלחו דרך ה־API רק הסכמה שכבר אספתם.
  • דיווח לפי אינדקס האיבר. כשאיבר כלשהו שולח subscriptions, תוצאת attributes כוללת גם subscriptions: { applied, skipped[] }, וכל דילוג מציין את index של האיבר, את listId, את channel ואת reason. מנוי שדולג אינו מכשיל את האיבר - האדם עדיין נוצר או מתעדכן.
{
"attributes": {
"processed": 1,
"failed": [],
"subscriptions": {
"applied": 0,
"skipped": [
{
"index": 0,
"listId": "3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d",
"channel": "email",
"reason": "Existing opt-out is not resurrected by user creation; use POST /subscriptions/contacts/:userId/lists/:listId for an explicit re-opt-in"
}
]
}
}
}

דוגמה לבקשה​

curl -X POST https://api-eu1.joryio.com/api/users/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"externalId": "user_123",
"email": "[email protected]",
"attributes": { "firstName": "John", "plan": "premium" }
},
"events": {
"externalId": "user_123",
"name": "Product Viewed",
"properties": { "productId": "SKU-456", "price": 49.99 }
},
"purchases": {
"orderId": "ord_789",
"externalId": "user_123",
"total": 129.90,
"currency": "USD",
"items": [
{ "productId": "SKU-456", "name": "Blue T-Shirt", "quantity": 1, "price": 129.90, "total": 129.90 }
]
}
}'

זה לא אותו דבר כמו שליחת anonymousId ב־/users/track. מעקב מעדכן או יוצר אדם; identify מקשר את הרשומה האנונימית לאדם מוכר. בהתחברות ראשונה - כשעדיין אין פרופיל עם ה־externalId הזה - הפרופיל האנונימי עצמו הופך לפרופיל המוכר ושומר על המזהה וההיסטוריה שלו. כשלאדם כבר יש פרופיל, הפרופיל האנונימי ממוזג אליו וההיסטוריה עוברת אליו. רישום anonymousId בקריאת מעקב לא ממזג דבר.

תגובה​

כל מערך מדווח בנפרד. אלמנט שגוי אחד לעולם לא מפיל את כל האצווה - כשלים מדווחים לפי אינדקס, וכל מה שתקין ממשיך לעבור.

{
"attributes": { "processed": 1, "failed": [] },
"events": { "processed": 1, "failed": [] },
"purchases": { "processed": 1, "failed": [] }
}

אזהרות: התקבל, אך אינו בר-השגה​

כתובת אימייל שגויה נשמרת ואינה נדחית - נקודת קצה זו משמשת אינטגרציות מסוג שגר-ושכח, שבהן שגיאת 400 שאיש אינו קורא פירושה אובדן הרשומה כולה. הכתובת מסומנת פנימית ומוחרגת מכל שליחת אימייל, כך שאינה יכולה לגרום להקפצה קשה.

מכיוון שהדבר אינו נראה בערך השמור, התשובה מציינת זאת:

{
"attributes": {
"processed": 2,
"failed": [],
"warnings": [
{
"index": 1,
"field": "email",
"reason": "Stored, but flagged undeliverable and excluded from email sends. Correct the address to make this contact reachable by email."
}
]
}
}

השדה warnings מופיע רק כשיש על מה לדווח. איש קשר עם אזהרה נוצר ומתעדכן כרגיל, וניתן להשיגו בכל ערוץ אחר - רק כתובת האימייל לעולם לא תקבל דבר.

מספרי טלפון​

שלחו מספרי טלפון עם + וקידומת מדינה

שלחו תמיד מספרי טלפון בפורמט בינלאומי מלא (E.164): +, קידומת המדינה ואז המספר - למשל +972501234567. מספר בלי + נקרא לפי מדינת ברירת המחדל למספרי טלפון של סביבת העבודה; אם לא הוגדרה, או שאפשר לקרוא את הספרות בשתי דרכים, הוא נשמר כלא מאומת ולא משמש להודעות. איך מטופלים מספרי טלפון

שלחו את phone בפורמט בינלאומי (+972501234567). מספר שנכתב ללא קידומת מדינה (0501234567) נקרא לפי מדינת ברירת המחדל למספרי טלפון של סביבת העבודה - עם ישראל הוא נשמר כ-+972501234567. Joryio אף פעם לא מנחש מדינה: אם לא הוגדרה מדינת ברירת מחדל, מספר כזה לא מומר.

טלפון שאינו מספר תקין נשמר, לא נמחק: הוא נשמר בשדה נפרד של איש הקשר, phoneUnverified, והטלפון הקיים (phone) של איש הקשר אינו משתנה. מספר לא מאומת לעולם אינו משמש לשליחת הודעות, לחסימות, להתאמת אנשי קשר או למסנני טלפון בסגמנטים. שליחת טלפון תקין בהמשך מחליפה אותו. ספרות הכוללות את קידומת המדינה ללא + (972501234567) מתקבלות כשהן תקינות - חוץ ממחרוזת של 10 ספרות, שיכולה להיות מספר מקומי או אמריקאי, ולכן נשמרת כלא מאומתת.

כשמוגדרת מדינת ברירת מחדל, ספרות שהן מספר תקין בשתי הדרכים - כמספר מקומי של אותה מדינה וכמספר של מדינה אחרת עם הקידומת שלה (13123456789 עם סין הוא נייד סיני וגם +1 312...) - הן דו-משמעיות: Joryio אף פעם לא בוחרת אחת מהן, והמספר נשמר כלא מאומת. שלחו מספרים כאלה עם +.

התגובה מדווחת על כך באותו מערך warnings:

{
"attributes": {
"processed": 1,
"failed": [],
"warnings": [
{
"index": 0,
"field": "phone",
"reason": "Phone saved as unverified and not used for messages - the number is not valid as written. Add the country code (for example +972501234567), or set a default country for phone numbers in workspace settings."
}
]
}
}

אנשי קשר שה-API מחזיר כוללים את phoneUnverified כשיש להם כזה. ייבוא קבצים וסנכרון ממחסן נתונים פועלים לפי אותם כללים.

בדף איש הקשר אפשר לתקן מספר לא מאומת (הוא נשמר כטלפון - אם לאיש הקשר כבר יש טלפון, הוא מוחלף, והדף מציין זאת קודם) או למחוק אותו. דרך ה-API, PUT /users/{userId} עם {"clearPhoneUnverified": true} מוחק את המספר הלא מאומת ומשאיר את phone כפי שהוא.

זיהוי משתמש (Identify)​

קישור התנהגות שנרשמה תחת סשן אנונימי לאדם מזוהה.

שדהסוגחובהתיאור
anonymousIdמחרוזתכןהסשן שההתנהגות נרשמה תחתיו
externalIdמחרוזתכןהמזהה שלכם לקישור
attributesאובייקטלאשדות פרופיל להגדרה תוך כדי הזיהוי
curl -X POST https://api-eu1.joryio.com/api/users/identify \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "anonymousId": "anon_abc123", "externalId": "user_123" }'

מכשירי פוש​

חיבור טוקנים של פוש בכמות - המסלול להגירה מספק אחר.

POST /api/users/track
Content-Type: application/json

{
"devices": [
{ "externalId": "user-123", "platform": "ios", "pushToken": "a1b2c3…", "appId": "<ios-app-id>",
"deviceInfo": { "model": "iPhone15,2" } },
{ "externalId": "user-456", "platform": "android", "pushToken": "d4e5f6…", "appId": "<android-app-id>" }
]
}
שדהטיפוסחובהתיאור
externalIdstringכןהמזהה שלכם לאדם. שלחו userId במקום אם יש לכם את המזהה הפנימי של Joryio
platformios | android | webכן
pushTokenstringכןהטוקן בדיוק כפי שהספק הקודם שמר אותו
appIdstringלא*לאיזו מהאפליקציות שלכם שייך הטוקן. שליחות קמפיין מסננות לפי השדה הזה, כך שטוקן שיובא בלעדיו נשמר אך לעולם לא נמצא בשימוש. אופציונלי כשיש בסביבת העבודה בדיוק אפליקציה אחת בפלטפורמה הזו - אז הוא נפתר עבורכם. חובה כשיש כמה. את המזהים אפשר לקבל מ־GET /apps
deviceInfoobjectלאדגם, גרסת מערכת, גרסת אפליקציה - חופשי

עד 1000 מכשירים לבקשה. מכשירים מוחלים אחרי attributes, כך שקריאה אחת יכולה גם ליצור את האנשים וגם לחבר את הטוקנים שלהם.

איש קשר שאינו קיים גורם לכישלון של אותו פריט ולא נוצר - מכשיר ללא פרופיל יכול לקבל פוש ואינו שייך לאיש. שלחו אותו ב־attributes באותה בקשה.

מה באמת שורד הגירה​

לא הכל, וזה קובע את התוכנית יותר מה־API:

  • iOS / APNs - טוקנים מונפקים לפי (bundle id, סביבה), לא לפי ספק. אותו bundle id וה־.p8 שלכם, והם עוברים.
  • Android / FCM - טוקנים משויכים ל־sender של Firebase. אותו פרויקט והם עובדים; פרויקט אחר וכולם ממילא כבר מתים.
  • Web push - מנוי קשור למפתח ה־VAPID שיצר אותו. הוא לא יכול לעבור. את המשתמשים האלה צריך לבקש מחדש, מה שלא יבטיח שום API.

טוקן ששייך כבר למישהו אחר​

הוא עובר. טוקן פוש מזהה מכשיר, לא אדם, והתקנות מחדש, מכשירים שנמכרו וטאבלטים משותפים הם מצב רגיל - ולכן מי שמחזיק במכשיר הוא מי שמקבל את הפוש.

המעבר נרשם בשני הצדדים. המכשיר אצל איש הקשר הקודם מסומן ב־deactivatedReason: "reassigned" יחד עם reassignedToUserId ו־deactivatedAt, כך שלשאלה "למה האדם הזה הפסיק לקבל פוש?" יש תשובה, במקום שורה לא־פעילה שיכולה להיות גם הסרת התקנה.

הסכמה ואימות​

רישום טוקן לעולם לא מעניק הסכמה לפוש. החזקת טוקן מספק קודם אינה ראיה להרשאה, ולכן כללי המנוי לא משתנים - חסימות עדיין חלות, והענקת הסכמה נשארת פעולת ציות. זהו אותו כלל שייבוא קובץ פועל לפיו.

טוקנים מתקבלים כמות שהם ולא נבדקים. טוקן נחשב תקין עד שמשלוח נכשל, ואז מסלול המשלוח משבית אותו.

Get User by ID​

שליפת פרופיל המשתמש ומאפייניו. קיימים שני מסלולי חיפוש:

  • GET /users/by-user-id/:userId - חיפוש לפי המזהה שלכם (ה־externalId שאיתו יצרתם את המשתמש). זה המסלול המומלץ לאינטגרציות.
  • GET /users/:userId - חיפוש לפי המזהה הפנימי של Joryio (ה־id בן 24 תווי hex שמוחזר בתגובות).

Endpoint​

GET /users/by-user-id/:userId

פרמטרי נתיב​

פרמטרסוגתיאור
userIdstringהמזהה החיצוני שלכם (externalId)

דוגמת בקשה​

curl -X GET https://api-eu1.joryio.com/api/users/by-user-id/user_123 \
-H "Authorization: Bearer jry_live_your_api_key"

תגובה​

{
"id": "665f1e2a9b3c4d5e6f7a8b9c",
"userId": "665f1e2a9b3c4d5e6f7a8b9c",
"externalId": "user_123",
"email": "[email protected]",
"phone": "+1234567890",
"attributes": {
"firstName": "John",
"lastName": "Doe",
"plan": "premium",
"lifetimeValue": 1250.50
},
"segments": [],
"rndBucket": 4821,
"createdVia": "woocommerce",
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-20T14:20:00.000Z"
}

List Users​

רשימת כל המשתמשים עם עימוד. התוצאות ממוינות לפי עדכון אחרון - אין תחביר סינון לפי מאפיינים או מיון בנקודת קצה זו (השתמשו בסגמנטים לחיתוך לפי מאפיינים, או ב־GET /users/search?query=... לחיפוש לפי שם/אימייל/טלפון/מזהה).

Endpoint​

GET /users

פרמטרי שאילתה​

פרמטרסוגברירת מחדלתיאור
limitnumber50תוצאות לעמוד (לכל היותר 1000)
offsetnumber0מספר משתמשים לדילוג
emailstring-התאמה מדויקת, ללא תלות באותיות גדולות/קטנות
externalIdstring-התאמה מדויקת למזהה שלכם. GET /users/by-user-id/:userId מחזיר את הרשומה הבודדת ישירות
userIdstring-התאמה מדויקת למזהה הפנימי של Joryio (24 תווים הקסדצימליים)
qstring-התאמה חלקית: תחילית באימייל, ב־externalId ובשם; ספרות בכל מקום בטלפון; ערך מלא במזהה 24 תווים או ב־anonymousId. מתעלמים ממנו כשקיים פרמטר מדויק. ראו איתור אדם

דוגמת בקשה​

curl -X GET "https://api-eu1.joryio.com/api/users?limit=50&offset=0" \
-H "Authorization: Bearer jry_live_your_api_key"

תגובה​

{
"data": [
{
"id": "665f1e2a9b3c4d5e6f7a8b9c",
"userId": "665f1e2a9b3c4d5e6f7a8b9c",
"externalId": "user_123",
"email": "[email protected]",
"attributes": {
"plan": "premium"
}
},
{
"id": "665f1e2a9b3c4d5e6f7a8b9d",
"userId": "665f1e2a9b3c4d5e6f7a8b9d",
"externalId": "user_456",
"email": "[email protected]",
"attributes": {
"plan": "premium"
}
}
],
"pagination": {
"total": 156,
"limit": 50,
"offset": 0,
"hasMore": true
}
}

Update User​

עדכון אימייל, טלפון או מאפיינים של משתמש בלי להחליף את הפרופיל כולו. המאפיינים מתמזגים לפי מפתח. משתמשים ב־PUT (אין נתיב PATCH):

  • PUT /users/by-user-id/:userId - עדכון לפי המזהה שלכם.
  • PUT /users/:userId - עדכון לפי המזהה הפנימי של Joryio.

Endpoint​

PUT /users/by-user-id/:userId

גוף בקשה​

שדהסוגחובהתיאור
emailstringלאכתובת אימייל חדשה
phonestringלאמספר טלפון חדש
attributesobjectלאמאפיינים למיזוג (עדכון לפי מפתח)

דוגמת בקשה​

curl -X PUT https://api-eu1.joryio.com/api/users/by-user-id/user_123 \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"attributes": {
"plan": "enterprise",
"mrr": 499
}
}'

תגובה​

אובייקט המשתמש המעודכן מוחזר ישירות:

{
"id": "665f1e2a9b3c4d5e6f7a8b9c",
"userId": "665f1e2a9b3c4d5e6f7a8b9c",
"externalId": "user_123",
"attributes": {
"firstName": "John",
"plan": "enterprise",
"mrr": 499,
"signupDate": "2024-01-15T10:30:00Z"
},
"updatedAt": "2024-01-20T15:30:00.000Z"
}

Delete User​

מחיקת משתמש. המשתמש מועבר לסל מיחזור (מאורכב עם רישום ביקורת) וניתן לשחזר אותו - הוא אינו נמחק לצמיתות.

Endpoint​

DELETE /users/:userId

פרמטר הנתיב הוא המזהה הפנימי של Joryio. דורש את ההרשאה users:delete. אפשר להעביר ?reason= אופציונלי כדי לתעד את הסיבה ברישום הביקורת של המחיקה.

דוגמת בקשה​

curl -X DELETE "https://api-eu1.joryio.com/api/users/665f1e2a9b3c4d5e6f7a8b9c?reason=duplicate" \
-H "Authorization: Bearer jry_live_your_api_key"

תגובה​

204 No Content - גוף התגובה ריק.

הערות​

  • המשתמש מועבר לסל מיחזור וניתן לשחזר אותו - ראו List Deleted Users. כל מחיקה נרשמת עם רישום ביקורת של מי / מתי / איך / למה. (השחזור מתבצע כרגע דרך endpoint פנימי - פנו לתמיכה כדי לשחזר איש קשר שנמחק.)
  • המשתמש מוסר מהסגמנטים וקמפיינים פעילים מפסיקים לכוון אליו מיידית.
  • היסטוריית האירועים נשמרת, כך שמשתמש משוחזר שומר על ההיסטוריה המלאה שלו.
  • כדי למחוק לצמיתות משתמש וכל הנתונים שלו (הזכות למחיקה לפי GDPR), השתמשו בזרימת מחיקת הנתונים (DSR) במקום זאת - היא בלתי הפיכה ומהווה פעולה שונה.

List Deleted Users​

רשימת סל המיחזור - משתמשים שנמחקו, יחד עם רישום הביקורת של המחיקה (מי, מתי, איך, למה).

Endpoint​

GET /users/deleted

דורש את ההרשאה users:read.

פרמטרי שאילתה​

פרמטרסוגברירת מחדלתיאור
querystring-אופציונלי. התאמה לפי תחילית email/externalId, טלפון או מזהה המשתמש המקורי
limitnumber50תוצאות לכל היותר (לכל היותר 200)
offsetnumber0מספר תוצאות לדילוג

דוגמת בקשה​

curl "https://api-eu1.joryio.com/api/users/deleted?limit=50" \
-H "Authorization: Bearer jry_live_your_api_key"

תגובה​

{
"total": 1,
"items": [
{
"originalId": "665f1e2a9b3c4d5e6f7a8b9c",
"email": "[email protected]",
"phone": "+15551234567",
"externalId": "usr_1001",
"deletedAt": "2026-08-09T03:00:30.000Z",
"deletedBy": "[email protected]",
"deletedVia": "api",
"deletedReason": "duplicate",
"restoredAt": null
}
]
}

Bulk Operations (array body)​

אין נקודת קצה נפרדת לפעולות באצווה, ואין נתיב שמקבל מערך חשוף של משתמשים. אצוות עוברות דרך POST /users/track: כל אחד מהשדות שלו - attributes, events, purchases, devices - מקבל או אובייקט בודד או מערך, כך שבקשה אחת יכולה לשאת אנשים רבים, את הפעילות שלהם ואת המנויים שלהם לרשימות.

Endpoint​

POST /users/track
Content-Type: application/json

{ "attributes": [ { ... }, { ... } ], "events": [ { ... } ] }

גוף בקשה​

גוף הבקשה חייב להיות אובייקט JSON עם לפחות אחד מהשדות attributes, events, purchases, devices. מערך חשוף ([ {...}, {...} ]) נדחה עם 400 "Body must be an object with any of: attributes, events, purchases, devices.", וכך גם אובייקט ריק.

כל איבר במערך באותו מבנה כמו הצורה של אובייקט בודד המתוארת במעקב. כל איבר עובר תיקוף בנפרד, ומפתחות לא מוכרים נדחים ולא מתעלמים מהם.

דוגמת בקשה​

יצירה או עדכון של שני אנשים, צירוף הראשון לרשימה ורישום אירוע הרשמה לכל אחד, בקריאה אחת. attributes מוחלים לפני events, כך שהאירועים יכולים להתייחס לאנשים שנוצרו באותה בקשה.

curl -X POST https://api-eu1.joryio.com/api/users/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"attributes": [
{
"externalId": "user_001",
"email": "[email protected]",
"attributes": { "firstName": "John", "plan": "free" },
"subscriptions": [
{ "listId": "3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d", "channel": "email" }
]
},
{
"externalId": "user_002",
"email": "[email protected]",
"attributes": { "firstName": "Jane", "plan": "premium" }
}
],
"events": [
{ "externalId": "user_001", "name": "Signed Up", "properties": { "source": "landing_page" } },
{ "externalId": "user_002", "name": "Signed Up", "properties": { "source": "referral" } }
]
}'

תגובה​

סיכום אחד לכל מערך. processed סופר את האיברים שהוחלו; failed מפרט את השאר לפי האינדקס שלהם באותו מערך. warnings מופיע רק כשאיבר נשמר אבל דורש תשומת לב (ראו מעקב).

{
"attributes": {
"processed": 2,
"failed": [],
"subscriptions": { "applied": 1, "skipped": [] }
},
"events": { "processed": 2, "failed": [] },
"purchases": { "processed": 0, "failed": [] },
"devices": { "processed": 0, "failed": [] }
}

תגובה עם כשלים​

איבר פגום אחד לעולם אינו מכשיל את האצווה - כל מה שתקין עדיין מעובד:

{
"attributes": {
"processed": 1,
"failed": [
{ "index": 1, "reason": "property emial should not exist" }
],
"subscriptions": {
"applied": 0,
"skipped": [
{
"index": 0,
"listId": "3f9d2c1e-7a54-4b2e-9c1d-8e6f5a4b3c2d",
"channel": "email",
"reason": "Existing opt-out is not resurrected by user creation; use POST /subscriptions/contacts/:userId/lists/:listId for an explicit re-opt-in"
}
]
}
},
"events": {
"processed": 1,
"failed": [
{ "index": 0, "reason": "name should not be empty" }
]
},
"purchases": { "processed": 0, "failed": [] },
"devices": { "processed": 0, "failed": [] }
}

מגבלות​

  • לכל בקשה: 1000 attributes, 500 events, 500 purchases, 1000 devices
  • בקשה שחורגת ממכסה נדחית עם 400 (לדוגמה "events accepts at most 500 items per request (received 600). Split the batch.") ולא נחתכת - שום דבר ממנה אינו מעובד
  • לאצוות גדולות יותר, פצלו לבקשות עוקבות של עד המכסה לכל שדה. ניסיון חוזר בטוח: /users/track הוא upsert לפי externalId, רכישות ממופתחות לפי orderId, ואירועים יכולים לשאת clientEventId למניעת כפילויות
מנויים לרשימות נשלחים באותה קריאה

כל איבר של attributes יכול לשאת subscriptions (ראו מנויים לרשימות). בתגובה שלמעלה, איבר 0 של attributes צורף לרשימה; בתגובה עם הכשלים, ביטול ההרשמה הקיים שלו נשמר ודווח, והאדם עדיין עודכן. כדי לשנות מנוי מאוחר יותר, השתמשו ב־POST /subscriptions/contacts/:userId/lists/:listId.

צורת המערך מיועדת לאצוות תכנותיות מצד השרת שלכם. עבור קבצים והגירות מלאות, אל תבנו לולאות אצווה ידניות - השתמשו ב-Bulk Import בלוח הבקרה (CSV/JSON עם מיפוי עמודות, מניעת כפילויות, כללי הסכמה ודוח שגיאות) או בסנכרון מחסן נתונים לטעינות חוזרות.


User Events​

Get User's Events​

שליפת כל האירועים עבור משתמש ספציפי.

Endpoint​

GET /users/:userId/events

פרמטר הנתיב הוא המזהה הפנימי של Joryio.

פרמטרי שאילתה​

פרמטרסוגברירת מחדלתיאור
limitnumber50מספר אירועים להחזרה (לכל היותר 1000)
offsetnumber0היסט לעימוד
startDatestring-סינון אירועים אחרי תאריך זה (ISO 8601)
endDatestring-סינון אירועים לפני תאריך זה (ISO 8601)
eventNamestring-סינון לפי שם אירוע
groupBySessionbooleanfalseהחזרה גם של האירועים מקובצים לפי הפעלה
includeJourneyStepsbooleantruefalse משמיט את אירועי שלבי המסע (canvas.step.entered / canvas.step.left) מהאירועים ומהסך

דוגמת בקשה​

curl -X GET "https://api-eu1.joryio.com/api/users/665f1e2a9b3c4d5e6f7a8b9c/events?limit=20" \
-H "Authorization: Bearer jry_live_your_api_key"

תגובה​

שדות האירוע ב־snake_case (הם מגיעים ממחסן האנליטיקה):

{
"data": [
{
"event_id": "9b2f6c1e-4a8d-4f0b-9c3d-2e1f5a6b7c8d",
"event_name": "Order Completed",
"properties": {
"orderId": "order_456",
"total": 99.99
},
"timestamp": "2024-01-20 14:30:00"
},
{
"event_id": "1c3e5a7b-9d2f-4b6c-8e0a-3f5d7b9c1e2a",
"event_name": "Page Viewed",
"properties": {
"page": "/pricing"
},
"timestamp": "2024-01-20 14:25:00"
}
],
"total": 156,
"limit": 20,
"offset": 0
}

מאפיינים נפוצים​

שדות ומאפיינים מיוחדים​

email ו־phone הם שדות פרופיל ברמה העליונה (לא מאפיינים) - שלחו אותם ברמה העליונה של גוף הבקשה.

למאפיינים הבאים יש משמעות מיוחדת ב־Joryio:

מאפייןסוגתיאור
firstNamestringשם פרטי (משמש בחיפוש ובתצוגה)
lastNamestringשם משפחה (משמש בחיפוש ובתצוגה)
languagestringשפה מועדפת (מוגדרת אוטומטית על ידי ה־SDKs כשזמינה)
timezonestringאזור זמן בפורמט IANA (מפעיל שעות שקט ומשלוח מסעות לפי זמן מקומי)
countrystringקוד מדינה (מאונדקס לסגמנטציה)

מאפיינים מותאמים​

אפשר להוסיף מספר בלתי מוגבל של מאפיינים מותאמים:

{
"attributes": {
"plan": "premium",
"mrr": 99,
"signupSource": "google_ads",
"lifetimeValue": 1250.50,
"tags": ["vip", "early-adopter"],
"preferences": {
"emailNotifications": true,
"smsNotifications": false
}
}
}

סוגי נתונים נתמכים​

  • String: "premium"
  • Number: 99.99
  • Boolean: true / false
  • Date: "2024-01-15T10:30:00Z" (ISO 8601)
  • Array: ["tag1", "tag2"]
  • Object: { "nested": "value" }

תגובות שגיאה​

כל השגיאות משתמשות בגוף השגיאה הסטנדרטי - ראו סקירת API: תגובת שגיאה.

400 Bad Request​

{
"statusCode": 400,
"message": "Cannot create or update a user without a valid identifier (externalId or email to create; userId only updates an existing user)",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/users"
}

401 Unauthorized​

{
"statusCode": 401,
"message": "Invalid or expired API key",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/users"
}

404 Not Found​

{
"statusCode": 404,
"message": "User with userId 'user_123' not found",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/users/by-user-id/user_123"
}

שיטות עבודה מומלצות​

1. ניסיון חוזר בטוח - POST /users/track הוא Upsert​

POST /users/track ממופתח לפי ה־userId שלכם - ניסיון חוזר של אותה בקשה מעדכן את אותו פרופיל במקום ליצור כפילות. אין כותרת Idempotency-Key; פשוט שלחו את הבקשה שוב כפי שהיא בעת כשל רשת:

curl -X POST https://api-eu1.joryio.com/api/users/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-d '{...}'

2. שמות מאפיינים​

השתמשו בשמות עקביים ותיאוריים:

טוב:

{
"signupDate": "2024-01-15",
"lifetimeValue": 1250.50,
"plan": "premium"
}

לא טוב:

{
"sd": "2024-01-15",
"ltv": 1250.50,
"p": "premium"
}

3. פורמט מספר טלפון​

שלחו מספרי טלפון בפורמט E.164 - +, קידומת המדינה ואז המספר. ראו מספרי טלפון לגבי הטיפול במספר בלי +.

טוב: "+972501234567", "+12125551234" להימנע: "050-123-4567", "(212) 555-1234" - בלי + הם תלויים במדינת ברירת המחדל של סביבת העבודה


מגבלות קצב​

ל־Users API אין כיום מגבלות קצב קבועות לכל נקודת קצה - ראו סקירת API: מגבלות קצב להתנהגות ברמת הפלטפורמה ולטיפול בתגובות 429.


סטטיסטיקת אנשי קשר​

GET /users/stats
GET /users/stats/growth?days=30

מספרים להשוואה מול מקור האמת שלכם - אם בחנות יש 40,000 לקוחות וכאן כתוב 31,000, משהו לא מגיע. GET /users/stats/growth?days=30 מחזיר מערך JSON של { "date", "activeUsers", "newUsers" } - רשומה לכל יום (יום מקומי של מרחב העבודה, מהישן לחדש): activeUsers הם האנשים שעשו משהו באותו יום ו-newUsers אלה שפעילותם הראשונה הייתה באותו יום. days ברירת מחדל 30, מוגבל ל-1-365; אין פרמטר granularity - הסדרה תמיד יומית.

{
"total": 31204,
"thisWeek": 1840,
"lastWeek": 2110,
"growthPercent": -12.8,
"growthType": "down",
"lifetimeSessions": 96420,
"activeUsers30d": 7310,
"activeUsersPrev30d": 6980
}

total הוא מספר אנשי הקשר, ללא פרופילי סורקים. כל שאר המספרים סופרים אנשים שעשו משהו - קבלת הודעה אינה פעילות - והם null ולא 0 כשמאגר הניתוחים אינו זמין, כך שפער לעולם אינו מדווח כאפס. השבועות הם לפי אזור הזמן של סביבת העבודה ומתחילים ביום ראשון; thisWeek הוא שבוע חלקי מעצם הגדרתו, ולכן growthPercent נמוך בתחילת שבוע. lifetimeSessions סופר סשנים ייחודיים, לא אירועי תחילת סשן.

צעדים הבאים​

זיהוי מחדש של סוגי מאפיינים​

נקודת קצה​

POST /attributes/rescan-types

סוגי מאפיינים שזוהו נקבעים פעם אחת, מדגימה של אנשי הקשר שלכם, ואז נשמרים - כך שסוג לא נודד בין קריאות ככל שמגיעים נתונים חדשים. המחיר הוא שהחלטה עלולה להתיישן כשהנתונים משתנים באמת. נקודת הקצה הזו זורקת את ההחלטות השמורות כדי שייקבעו מחדש מהנתונים הנוכחיים.

גוף הבקשה​

שדהסוגנדרשתיאור
eventNamestringלאמגביל את הסריקה למאפייני אירוע אחד. בהיעדרו, נסרקים מאפייני אנשי הקשר של סביבת העבודה

סוגים שהצוות שלכם קבע לעולם אינם משתנים - החלטה של אדם אינה מתיישנת כי הנתונים השתנו. שדות סטנדרטיים (price, total, lastEventTime, ...) מקבלים סוג מהפלטפורמה ואינם מושפעים גם הם.

תגובה​

{ "success": true, "message": "Types will be detected again from current data" }
מתי לקרוא לזה

לעיתים רחוקות. ההחלטה השמורה היא כל העניין - היא מה שגורם לשכבה להתכוון היום לאותו דבר כמו אתמול. קראו לזה כשמשמעות של מאפיין באמת השתנתה במקור, לא לפי לוח זמנים.