# שליחת לידים ל-Pushulu — מדריך אינטגרציה מסמך לצוות/סוכן שמפתח **מערכת שולחת** (טופס, מערכת חתימות, אוטומציה, CRM חיצוני) וצריך להזרים לידים ל-Pushulu. אין צורך להכיר את הקוד של Pushulu — כל מה שצריך נמצא כאן. > **הערה למי שקורא את זה בתוך Claude Code:** המסמך הזה הוא ה-API contract. > אל תנחש שמות שדות ואל תמציא endpoints — הכל מתועד למטה. אם משהו לא מכוסה כאן, > עדיף לשאול מאשר לנחש. --- ## 1. ה-Endpoint ``` POST https://pushulu.com/api/leads/webhook/{UNIQUE_LINK} ``` `{UNIQUE_LINK}` הוא מזהה סודי באורך 32 תווים, **אחד לכל לקוח**, והוא מה שקובע לאיזה חשבון הליד נכנס. מוצאים אותו במערכת תחת **הגדרות חברה** (`/app/company-settings`). ### הלינק הוא סוד מי שמחזיק בו יכול להזריק לידים לחשבון. לכן: - **אל תכתוב אותו בקוד.** קרא אותו ממשתנה סביבה, למשל `PUSHULU_WEBHOOK_URL`. - אל תשלח אותו לדפדפן ואל תשים אותו ב-JS צד-לקוח. השליחה תמיד מהשרת. - אם דלף — צור לינק חדש בהגדרות החברה. הישן מפסיק לעבוד. ```bash # .env PUSHULU_WEBHOOK_URL=https://pushulu.com/api/leads/webhook/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` **אין header של אימות.** ה-URL עצמו הוא האישור. אל תוסיף `Authorization` — הוא ייענה. --- ## 2. פורמט הבקשה נתמכים שני content types: | Content-Type | מתי | |---|---| | `application/json` | ברירת המחדל, מומלץ | | `application/x-www-form-urlencoded` | טפסי HTML קלאסיים | גודל גוף מקסימלי: **10MB**. פרמטרים ב-query string גם נקראים וממוזגים עם הגוף, אבל **הגוף גובר** — עדיף לשלוח הכל בגוף ולא לערבב. ### דוגמה מינימלית ```bash curl -X POST "$PUSHULU_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{"name":"ישראל ישראלי","phone":"0501234567","email":"israel@example.com"}' ``` ### נדרש לפחות אחד מ: `name` / `phone` / `email` ליד בלי אף אחד משלושת אלה לא ייקלט כליד רגיל — הוא ייפול לתור אישור ידני. --- ## 3. שמות שדות שהמערכת מזהה אוטומטית אין צורך להתאים את המערכת השולחת. שלח את השמות הטבעיים שלך — Pushulu ממפה אותם לשדות הסטנדרטיים (קודם AI, ואם הוא לא זמין אז זיהוי לפי תבניות). אלה השמות המזוהים: | שדה במערכת | שמות נכנסים שמזוהים | |---|---| | `name` | `name`, `full_name`, `fname`, `first_name`, `contact`, `customer`, `שם`, `שם מלא`, `שם פרטי` | | `lastName` | `last_name`, `lname`, `surname`, `family_name`, `שם משפחה`, `משפחה` | | `phone` | `phone`, `phone_number`, `mobile`, `cell`, `tel`, `telephone`, `טלפון`, `נייד`, `פלאפון` | | `email` | `email`, `mail`, `e_mail`, `email_address`, `אימייל`, `מייל`, `דוא"ל` | | `company` | `company`, `company_name`, `business`, `organization`, `org`, `חברה`, `עסק` | | `message` | `message`, `msg`, `comment`, `note`, `notes`, `details`, `text`, `הודעה`, `הערה`, `פרטים` | | `source` | `source`, `utm_source`, `campaign`, `ref`, `referrer`, `מקור`, `קמפיין` | | `formName` | `form_name`, `form_title`, `page_title`, `שם הטופס` | | `formUrl` | `form_url`, `page_url`, `landing_url`, `referer`, `כתובת הטופס` | | **`link`** | `link`, `url`, `document`, `document_url`, `doc_url`, `signed_url`, `file_url`, `pdf_url`, `download_link`, `attachment`, `לינק`, `קישור`, `מסמך`, `מסמך חתום` | שדות שלא מזוהים **נשמרים ב-payload הגולמי** אבל לא מופיעים ככרטיס ליד. אם יש שדה שחשוב שיופיע — הכנס אותו ל-`message`, או בקש מיפוי ידני במסך `/app/field-mapping`. --- ## 4. שדה `link` — לינק לחיץ בליד זה השדה לכל URL שהמשתמש ירצה לפתוח מתוך הליד: **מסמך חתום**, קובץ, PDF, הקלטה, חשבונית, נספח. הוא מוצג כלינק לחיץ גם בכרטיס הליד וגם בפופאפ, ונפתח בטאב חדש. ```json { "name": "מסמך חתום - ישראל ישראלי", "phone": "0501234567", "email": "israel@example.com", "source": "signature-system", "link": "https://your-system.com/api/sign/file?token=abc123&kind=signed" } ``` ### כללים ל-`link` - **URL מלא ומוחלט**, כולל `https://`. כתובת בלי סכימה (`example.com/doc/1`) תושלם אוטומטית ל-`https://`, אבל עדיף לשלוח מלא. - **רק `http` / `https`.** כל סכימה אחרת (`javascript:`, `data:`, `file:`) נזרקת והשדה נשמר ריק. זה מכוון — הלינק מוצג כ-`href` בדפדפן. - **URL אחד בלבד.** אל תשלח שני URLים מופרדים ברווח. - הלינק צריך להיות **יציב וניתן לפתיחה מאוחר יותר**. לינק שפג אחרי דקות יראה שבור כשיפתחו את הליד מחר. אם צריך הרשאה — עדיף לינק חתום עם תוקף ארוך. - אל תשלח את אותו URL תחת כמה מפתחות (`link` + `document` + `document_url`). זה נסבל — הראשון נבחר — אבל מיותר. **בחר מפתח אחד.** ### `link` מול `formUrl` | | מתי | |---|---| | `link` | מה שהליד **נושא איתו** — המסמך החתום, הקובץ, ההקלטה | | `formUrl` | הדף/הטופס שממנו הליד **הגיע** | אם יש לך את שניהם, שלח את שניהם. רק `link` מוצג בממשק כרגע. --- ## 5. כל שדה — פעם אחת אל תשלח את אותו ערך תחת כמה מפתחות: ```json // ❌ מיותר — כל ערך פעמיים { "name": "ישראל", "שם מלא": "ישראל", "phone": "050…", "טלפון": "050…" } // ✅ { "name": "ישראל", "phone": "050…" } ``` שני מפתחות שממופים לאותו שדה → **הראשון נבחר והשני מושמט** (חוץ מ-`message`, שמצטבר אבל בלי לחזור על ערך שכבר קיים בו). זה מטופל בצד Pushulu, אבל שליחה כפולה מקשה לדעת איזה ערך ינצח — עדיף מפתח אחד לכל שדה. --- ## 6. מבנים מקוננים שנתמכים אין צורך לשטח בעצמך. שני המבנים האלה מזוהים ומפורקים אוטומטית: ```json // Facebook Lead Ads / Make.com — מערך של {name, values} { "field_data": [ { "name": "full_name", "values": ["ישראל ישראלי"] }, { "name": "email", "values": ["israel@example.com"] } ]} // Elementor וטפסים עם סוגריים מרובעים { "form_fields": { "name": "ישראל", "email": "israel@example.com" }, "form_id": "abc" } ``` שניהם מורמים לרמה העליונה. שדה שכבר קיים בחוץ **גובר** על אותו שם מבפנים. --- ## 7. תשובות השרת ### הצלחה — `201` ```json { "success": true, "message": "Lead processed successfully", "leadId": "CXcVTUb5QnqQEGi5J8eU", "webhookLead_id": "eOb1E1gY3ve9i2Q7vT8x", "duplicate": false, "score": 90, "processingTime": 332 } ``` ### התקבל אבל ממתין לאישור — `202` **זו לא שגיאה.** הליד נשמר ומחכה במסך אישור. אל תשלח שוב. ```json { "success": true, "status": "pending_approval", "pendingId": "…", "message": "…" } ``` מתי זה קורה: | `status` | סיבה | איפה מטפלים | |---|---|---| | `pending_approval` | מקור חדש שטרם מופה | מסך מיפוי השדות במערכת | | `pending_review` | סומן ע"י בדיקת אבטחה, או ע"י בקרת האיכות | מסך הלידים הממתינים במערכת | **מקור חדש עובר אישור פעם אחת.** אחרי האישור הראשון, כל הלידים הבאים מאותו מקור נכנסים ישירות. אם אתה משנה את מבנה ה-payload — ייתכן אישור נוסף. ### שגיאות | קוד | `error` | משמעות | מה לעשות | |---|---|---|---| | `404` | `Invalid webhook token` | הלינק שגוי או בוטל | בדוק את ה-URL בהגדרות החברה. **אל תנסה שוב** | | `403` | `access_denied` | ה-IP שלך חסום | פנה למנהל המערכת | | `429` | `rate_limited` | חריגה מקצב | האט, נסה שוב עם backoff | | `503` | `database_unavailable` | תקלה זמנית במסד | נסה שוב עם backoff | | `500` | `Processing failed` | שגיאה לא צפויה | נסה שוב, ואם חוזר — דווח | **כלל אצבע:** `4xx` (חוץ מ-429) = בעיה אצלך, אל תנסה שוב. `429`/`5xx` = נסה שוב. --- ## 8. מגבלת קצב הזרמה בקצב גבוה מדי מאותו IP תיענה ב-`429`. שליחה רגילה של לידים בזמן אמת לא מתקרבת לסף. בייבוא המוני: הוסף השהיה קצרה בין בקשות, וכבד את הכותרות `RateLimit-*` ו-`Retry-After` שחוזרות בתשובה. --- ## 9. כפילויות ליד עם אותו **טלפון או אימייל** שהתקבל **תוך 10 דקות** מסומן `duplicate: true`, אבל **עדיין נשמר** — לא נדחה. אצל הלקוח הוא מופיע עם תג "ליד כפול". זה לא מנגנון idempotency. אם ה-retry שלך שולח פעמיים, ייווצרו שני לידים. **שלח פעם אחת, ונסה שוב רק על `429`/`5xx`.** --- ## 10. בדיקת אבטחה כל payload עובר סינון לפני שמירה. תוכן שנראה כניסיון תקיפה נחסם ומועבר לסקירה ידנית במקום להישמר כליד. בפועל זה לא מפריע לתוכן לגיטימי — אבל **אל תשלח HTML גולמי, קוד, או תוכן שהמשתמש הקליד בלי סינון** בתוך שדות טקסט. שלח טקסט נקי. --- ## 11. מה לא לשלוח | שדה | למה | |---|---| | `user_id` | נקבע מהלינק הייחודי. שליחה שלו לא תשנה שיוך — רק תבלבל | | `company_id` | אותו דבר | | `Authorization` header | לא בשימוש, ה-URL הוא האישור | | סודות, טוקנים פנימיים, סיסמאות | שלח רק נתוני ליד. אין סיבה שסוד יעבור ב-payload | --- ## 12. דוגמה מלאה — מערכת חתימות ```js // נשלח אחרי שהמסמך נחתם. שים לב: מפתח אחד לכל שדה, URL מלא ב-link. async function sendSignedDocumentLead(signer, documentUrl) { const res = await fetch(process.env.PUSHULU_WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: signer.fullName, phone: signer.phone, email: signer.email, source: 'signature-system', link: documentUrl, // המסמך החתום — לינק לחיץ בליד message: [ `ת.ז / ח.פ: ${signer.taxId}`, `תאריך חתימה: ${signer.signedAt}` ].join(' | ') }) }); const body = await res.json().catch(() => ({})); if (res.status === 201) return { ok: true, leadId: body.leadId }; if (res.status === 202) return { ok: true, pending: body.status }; // נשמר, ממתין לאישור // 429 / 5xx → כדאי retry עם backoff. 4xx אחר → בעיה בבקשה, אל תנסה שוב. const retryable = res.status === 429 || res.status >= 500; throw Object.assign(new Error(body.message || `Pushulu ${res.status}`), { retryable }); } ``` --- ## 13. צ'קליסט לפני העלאה לפרודקשן - [ ] ה-URL נקרא ממשתנה סביבה, לא כתוב בקוד ולא חשוף לדפדפן - [ ] נשלח לפחות אחד מ-`name` / `phone` / `email` - [ ] כל שדה נשלח פעם אחת, במפתח אחד - [ ] `link` הוא URL מלא עם `https://`, יציב לאורך זמן - [ ] `source` מזהה את המערכת שלך, כדי שיהיה ברור מאיפה הליד הגיע - [ ] `201` ו-`202` מטופלים כהצלחה - [ ] retry רק על `429` / `5xx`, עם backoff - [ ] נשלח ליד בדיקה אחד, ואומת שהוא מופיע במערכת עם כל השדות ### בדיקה ידנית ```bash curl -i -X POST "$PUSHULU_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d '{"name":"בדיקה","phone":"0500000000","email":"test@example.com", "source":"integration-test","link":"https://example.com/doc.pdf", "message":"ליד בדיקה — אפשר למחוק"}' ``` מצופה `201` עם `leadId`, והליד אמור להופיע ב-`/app` תוך שניות (גם כהתראת פוש אם המכשיר מחובר). **אל תשכח למחוק את ליד הבדיקה.** --- **עודכן:** 2.8.2026 · שאלות: `simtob@gmail.com`