API — שליחת הודעות ואוטומציה

REST API אחד לשליחת הודעות וואטסאפ, ניהול אנשי קשר וקריאת שיחות. מיועד לחיבור מ-Make, Zapier, n8n או מהשרת שלכם.

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

לפני שמתחילים

אימות

כל קריאה נושאת מפתח API בכותרת Authorization. מפתח נוצר בהגדרות → מפתחות API, ומוצג פעם אחת בלבד — אצלנו נשמר רק ה-hash שלו, כך שאין דרך לשחזר אותו אחר כך. כל המפתחות מתחילים ב-qsk_.

Headers
Authorization: Bearer qsk_xxx
Content-Type: application/json

כתובת הבסיס: https://quick-chat.app · כל התשובות הן JSON · מפתח נושא הרשאות read ו/או write, וכל אנדפוינט למטה מציין מה הוא דורש.

חלון 24 השעות — הכלל שקובע הכל

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

אין צורך לממש את הלוגיקה הזו אצלכם: POST /api/v1/send מקבל גם טקסט וגם תבנית בבקשה אחת ובוחר לבד לפי מצב החלון, ומחזיר mode ו-billable כדי שתדעו מה קרה בפועל.

idempotencyKey — אל תדלגו על זה

Make, Zapier ו-n8n מבצעים ניסיון חוזר אוטומטי כשבקשה חוזרת ב-timeout. בלי מפתח ייחודי לכל שליחה, ניסיון חוזר כזה שולח ללקוח את אותה הודעה פעמיים — ומחייב אתכם פעמיים. השתמשו במזהה יציב מהמערכת שלכם, למשל lead-8832 או order-1234-ready. בקשה חוזרת עם אותו מפתח מחזירה duplicate: true ולא שולחת דבר.

שליחת הודעות

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

POST/api/v1/sendscope: writeהתחילו כאן

שליחה חכמה — בחרו לי איך

שולחים טלפון + טקסט + תבנית בבקשה אחת, ואנחנו בוחרים לפי מצב חלון 24 השעות: חלון פתוח → טקסט חופשי (חינם), חלון סגור → תבנית מאושרת (בתשלום). התשובה תמיד אומרת מה נשלח בפועל ואם זה חויב.

שדות ב-body

  • phonestringחובה

    מספר הנמען. מקבל פורמט ישראלי מקומי (0501234567) או E.164 (+972501234567).

  • textstring

    טקסט חופשי, עד 8000 תווים. נשלח רק אם חלון 24 השעות פתוח. נדרש אחד מבין text / template.

  • templateobject

    תבנית מאושרת: { name, language (ברירת מחדל "he"), variables: string[], buttons?: [{ index, subType, value }] }. זו הדרך היחידה להגיע ללקוח מחוץ לחלון.

  • mode"auto" | "template"

    ברירת מחדל auto — בוחר לפי מצב החלון. "template" כופה תבנית גם בתוך החלון, שימושי כשהכפתורים של התבנית הם העיקר ולא רק היכולת לפתוח שיחה.

  • idempotencyKeystring

    מפתח למניעת כפילות. חובה בפועל בכל תרחיש Make/Zapier — הם עושים retry אוטומטי על timeout, ובלי המפתח הלקוח יקבל את ההודעה פעמיים ותשלמו פעמיים.

  • contactNamestring

    שם לאיש הקשר. בשימוש רק אם הקריאה הזו יוצרת אותו.

  • channelIdstring

    נדרש רק אם לארגון מחובר יותר מערוץ WhatsApp רשמי אחד. אחרת נבחר הערוץ היחיד.

cURL
curl -X POST https://quick-chat.app/api/v1/send \
  -H "Authorization: Bearer qsk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "0501234567",
    "contactName": "דנה לוי",
    "text": "היי דנה, קיבלנו את הפרטים שלך!",
    "template": {
      "name": "your_template_name",
      "language": "he",
      "variables": ["דנה"]
    },
    "idempotencyKey": "lead-8832"
  }'
200 OK
{
  "ok": true,
  "mode": "template",
  "billable": true,
  "duplicate": false,
  "conversationId": "conv_...",
  "contactId": "cnt_...",
  "messageId": "msg_...",
  "conversationCreated": true,
  "window": {
    "open": false,
    "lastInboundAt": null,
    "expiresAt": null
  }
}
  • •שלחתם רק `text` והחלון סגור? תקבלו 409 עם code `window_closed` שמציין במפורש להוסיף `template`. מי שלא רוצה לשלם לעולם יכול להסתמך על ההתנהגות הזו.
  • •`billable: true` מציין שההודעה חויבה על ידי Meta. `duplicate: true` אומר שה-idempotencyKey תפס בקשה חוזרת — לא נשלחה הודעה ולא חויבתם.
  • •איש הקשר והשיחה נוצרים אוטומטית אם אינם קיימים, וההודעה מופיעה ב-Inbox כמו כל שיחה אחרת.
POST/api/v1/messages/templatescope: write

שליחת תבנית מאושרת

שולח תמיד תבנית, בלי לבדוק את מצב החלון. מתאים כשאתם יודעים בוודאות שאתם פונים ללקוח קר. אם אתם בונים תרחיש חדש — עדיף /api/v1/send.

שדות ב-body

  • phonestringחובה

    מספר הנמען.

  • namestringחובה

    שם התבנית בדיוק כפי שאושרה ב-WABA.

  • languagestring

    קוד שפה, ברירת מחדל "he".

  • variablesstring[]

    ערכים ל-{{1}}, {{2}}… לפי הסדר. עד 10.

  • buttonsobject[]

    ערכים לכפתורים דינמיים: [{ index, subType: "url" | "quick_reply", value }]. עד 3.

  • idempotencyKeystring

    מפתח למניעת כפילות, בדיוק כמו ב-/api/v1/send.

  • contactName / channelIdstring

    כמו ב-/api/v1/send.

cURL
curl -X POST https://quick-chat.app/api/v1/messages/template \
  -H "Authorization: Bearer qsk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "0501234567",
    "name": "your_template_name",
    "language": "he",
    "variables": ["דנה", "פיצה משפחתית"],
    "idempotencyKey": "order-1234-ready"
  }'
200 OK
{
  "ok": true,
  "duplicate": false,
  "billable": true,
  "conversationId": "conv_...",
  "contactId": "cnt_...",
  "messageId": "msg_...",
  "status": "SENT",
  "conversationCreated": true
}
  • •האנדפוינט הזה במכוון לא בודק את חלון 24 השעות — תבנית היא הדרך היחידה שMeta מתירה להגיע מחוצה לו, ולכן אין מה לבדוק.
POST/api/v1/messagesscope: write

טקסט חופשי או קובץ

שתי דרכי פנייה: `conversationId` כדי להשיב בתוך שיחה קיימת, או `phone` כדי לפנות לאדם. טקסט חופשי עובר רק בתוך חלון 24 השעות.

שדות ב-body

  • conversationIdstring

    מזהה שיחה קיימת. חובה בדיוק אחד מבין conversationId / phone.

  • phonestring

    פנייה לפי מספר — מאתר או יוצר איש קשר ושיחה.

  • textstring

    עד 8000 תווים. נדרש text או attachment.

  • attachmentobject

    { url, mime, filename?, size? } — קובץ בכתובת ציבורית.

  • contactName / channelIdstring

    בשימוש רק יחד עם phone.

cURL
curl -X POST https://quick-chat.app/api/v1/messages \
  -H "Authorization: Bearer qsk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "conversationId": "conv_xxxxxxxx",
    "text": "בדקנו — ההזמנה יוצאת היום"
  }'
200 OK
{
  "ok": true,
  "messageId": "msg_..."
}
  • •פנייה לפי `phone` כשהחלון סגור מחזירה 409 עם code `window_closed` ומפנה ל-/api/v1/messages/template. כישלון רועש עדיף על הודעה שנעלמת בשקט באמצע אוטומציה.
GET/api/v1/templatesscope: read

רשימת התבניות המאושרות

מחזיר את התבניות שעל ה-WABA של הארגון, כולל מספר המשתנים והכפתורים של כל אחת — כדי שתרחיש חיצוני יוכל לבחור תבנית בלי להיכנס ל-WhatsApp Manager.

פרמטרים ב-query

  • channelIdstring

    נדרש רק אם מחוברים כמה ערוצי WhatsApp רשמיים.

  • statusstring

    סינון, למשל APPROVED.

cURL
curl "https://quick-chat.app/api/v1/templates?status=APPROVED" \
  -H "Authorization: Bearer qsk_xxx"
200 OK
{
  "channelId": "chn_...",
  "templates": [
    {
      "name": "order_ready",
      "language": "he",
      "status": "APPROVED",
      "category": "UTILITY",
      "body": "היי {{1}}, ההזמנה {{2}} מוכנה",
      "variableCount": 2,
      "buttons": [
        { "index": 0, "type": "URL", "text": "למעקב", "dynamic": true }
      ]
    }
  ]
}
  • •`variableCount` הוא בדיוק האורך שצריך להיות ל-`variables` בשליחה. `dynamic: true` על כפתור אומר שחייבים לספק לו value.

אנשי קשר

יצירה, עדכון ותיוג — כדי שהליד מהקמפיין ייכנס למערכת עם התגיות הנכונות.

POST/api/v1/contactsscope: write

יצירה או עדכון (upsert)

מוסיף איש קשר, או מעדכן קיים. הדדופ הוא לפי טלפון ואז לפי אימייל — בדיוק כמו בייבוא CSV מהממשק.

שדות ב-body

  • namestring

    שם מלא.

  • phonestring

    מספר טלפון.

  • emailstring

    כתובת אימייל.

  • tagsstring[]

    עד 20 תגיות. דורס את הרשימה הקיימת.

cURL
curl -X POST https://quick-chat.app/api/v1/contacts \
  -H "Authorization: Bearer qsk_xxx" \
  -H "Content-Type: application/json" \
  -d '{"name":"דנה לוי","phone":"+972501234567","tags":["ליד קמפיין"]}'
200 OK
{
  "contact": {
    "id": "cnt_...",
    "name": "דנה לוי",
    "primaryPhone": "+972501234567",
    "primaryEmail": null,
    "tags": ["ליד קמפיין"]
  },
  "created": true
}
  • •נדרש לפחות אחד מבין phone / email / name. יצירה מחזירה 201, עדכון מחזיר 200.
  • •`tags` כאן דורס. כדי להוסיף תגית בלי למחוק קיימות — /api/v1/contacts/tag.
POST/api/v1/contacts/tagscope: write

הוספה והסרה של תגיות

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

שדות ב-body

  • phone / email / contactIdstringחובה

    נדרש אחד מהם לאיתור איש הקשר.

  • addstring[]

    תגיות להוספה, עד 20. נדרש add או remove.

  • removestring[]

    תגיות להסרה.

  • namestring

    שם, בשימוש רק אם הקריאה יוצרת את איש הקשר.

cURL
curl -X POST https://quick-chat.app/api/v1/contacts/tag \
  -H "Authorization: Bearer qsk_xxx" \
  -H "Content-Type: application/json" \
  -d '{"phone":"0501234567","add":["ליד חם"],"remove":["ליד קר"]}'
200 OK
{
  "ok": true,
  "contactId": "cnt_...",
  "tags": ["ליד חם"],
  "created": false
}
  • •איש קשר חדש נוצר דרך פותר הזהויות, כדי לא לפצל אדם שכבר מוכר לנו מערוץ אחר. לכן נדרש לפחות ערוץ מחובר אחד בארגון.
  • •`contactId` שלא נמצא מחזיר 404 — לא ניצור איש קשר תחת מזהה מומצא.
GET/api/v1/contactsscope: read

רשימת אנשי קשר

עימוד לפי cursor על תאריך היצירה, מהחדש לישן.

פרמטרים ב-query

  • limitnumber

    ברירת מחדל 50, מקסימום 100.

  • cursorstring

    ה-createdAt של הפריט האחרון בעמוד הקודם (ISO-8601).

cURL
curl "https://quick-chat.app/api/v1/contacts?limit=50" \
  -H "Authorization: Bearer qsk_xxx"
200 OK
{
  "items": [ { "id": "cnt_...", "name": "דנה לוי", "primaryPhone": "+9725...", "tags": [] } ],
  "nextCursor": "2026-09-20T10:11:12.000Z"
}
  • •`nextCursor: null` מציין שהגעתם לעמוד האחרון.

שיחות וחשבון

קריאה בלבד — למשיכת מצב למערכת שלכם ולבדיקת מכסות לפני ריצה גדולה.

GET/api/v1/conversationsscope: read

רשימת שיחות

עימוד לפי lastMessageAt, עם סינון לפי סטטוס ומשויך.

פרמטרים ב-query

  • statusstring

    OPEN | PENDING | RESOLVED | SNOOZED.

  • assignedToUserIdstring

    רשימה מופרדת בפסיקים, או "null" לשיחות לא משויכות.

  • limit / cursornumber / string

    ברירת מחדל 50, מקסימום 100. ה-cursor הוא lastMessageAt.

cURL
curl "https://quick-chat.app/api/v1/conversations?status=OPEN&limit=20" \
  -H "Authorization: Bearer qsk_xxx"
200 OK
{
  "items": [
    {
      "id": "conv_...",
      "status": "OPEN",
      "contactId": "cnt_...",
      "channelId": "chn_...",
      "assignedToUserId": null,
      "lastMessageAt": "2026-09-22T08:00:00.000Z",
      "lastInboundAt": "2026-09-22T08:00:00.000Z",
      "firstResponseSeconds": 42,
      "unreadCount": 1
    }
  ],
  "nextCursor": null
}
GET/api/v1/me

בדיקת מפתח ומכסות

מאמת את המפתח ומחזיר כמה קריאות ותבניות נשארו לכם. הקריאה הזו לא צורכת מהמכסה — תרחיש שבודק את המכסה של עצמו לא אמור להיות זה שמחסל אותה.

cURL
curl https://quick-chat.app/api/v1/me \
  -H "Authorization: Bearer qsk_xxx"
200 OK
{
  "organizationId": "org_...",
  "organizationName": "קוויק פוד",
  "scopes": ["read", "write"],
  "apiKeyId": "key_...",
  "limits": {
    "apiRequests": { "limit": 120, "used": 3, "remaining": 117, "window": "minute", "resetAt": "..." },
    "templateSends": { "limit": 125, "used": 40, "remaining": 85, "window": "day", "resetAt": "..." }
  }
}
  • •קראו לזה פעם אחת לפני לולאה על מאות נמענים — עדיף לגלות שנגמרה המכסה מראש מאשר מ-429 באמצע.

מגבלות קצב

  • 120 קריאות API לדקה לארגון. חריגה מחזירה 429 עם כותרת retry-after.
  • 125 שליחות תבנית ליום לארגון. המספר מכוון בכוונה מתחת לתקרה ההתחלתית של Meta (250 שיחות יזומות ב-24 שעות): עדיף שאוטומציה שיצאה משליטה תיתקל במונה שלנו, שמחזיר 429 ולא עולה כלום, מאשר בתקרה של Meta — שם כל הודעה שעברה כבר חויבה וגם פוגעת בדירוג האיכות של המספר.
  • GET /api/v1/me אינו נספר במכסה, כדי שבדיקת מכסה לא תהיה הדבר שמכלה אותה.
  • שתי המגבלות ניתנות להעלאה פר ארגון. אם הנפח האמיתי שלכם גדול יותר — פנו אלינו ונעלה אותן לפני הקמפיין, לא באמצעו.

קודי שגיאה

כל שגיאה מחזירה { error, code? } — השדה error הוא טקסט בעברית שאפשר להציג למשתמש, ו-code הוא המזהה היציב שכדאי לבנות עליו לוגיקה.

  • 401

    הכותרת Authorization חסרה, לא בפורמט Bearer, או שהמפתח בוטל / פג.

    מה עושים: ודאו `Authorization: Bearer qsk_...`. מפתח שבוטל מפסיק לעבוד תוך דקה לכל היותר.

  • 403

    למפתח אין את ה-scope הנדרש (read או write).

    מה עושים: צרו מפתח חדש עם ההרשאות הנכונות.

  • 400invalid_body

    גוף הבקשה לא עבר ולידציה. השדה `issues` מפרט בדיוק מה נכשל.

    מה עושים: קראו את `issues` — הוא מציין את שם השדה והציפייה.

  • 400invalid_phone

    המספר לא ניתן לנרמול.

    מה עושים: שלחו 0501234567 או +972501234567.

  • 400no_channel

    אין בארגון ערוץ WhatsApp רשמי מחובר.

    מה עושים: חברו ערוץ בהגדרות → ערוצים.

  • 400ambiguous_channel

    מחוברים כמה ערוצי WhatsApp והבקשה לא ציינה באיזה להשתמש.

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

  • 404channel_not_found

    ה-channelId לא קיים בארגון, או שאינו ערוץ WhatsApp.

    מה עושים: בדקו את המזהה מול רשימת הערוצים.

  • 409window_closed

    חלון 24 השעות סגור וניסיתם לשלוח טקסט חופשי. Meta הייתה דוחה את ההודעה ממילא.

    מה עושים: הוסיפו `template` לבקשה, או עברו ל-/api/v1/messages/template.

  • 409channel_disconnected

    הערוץ קיים אבל אינו מחובר כרגע.

    מה עושים: בדקו את הערוץ בהגדרות — בדרך כלל טוקן שפג.

  • 409channel_incomplete

    לערוץ חסר WABA ID או טוקן.

    מה עושים: חברו את הערוץ מחדש.

  • 429rate_limited

    חריגה ממגבלת הקצב. התשובה כוללת `limit` ו-`resetAt`, וכן כותרות retry-after ו-x-ratelimit-*.

    מה עושים: המתינו לפי `retry-after`. אם המכסה היומית לתבניות קטנה מדי לנפח שלכם — נעלה אותה לארגון.

  • 502send_failed

    Meta דחתה את השליחה. השגיאה כוללת את הסיבה שהתקבלה מהם.

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

מתכון: ליד מקמפיין → הודעת וואטסאפ

  1. הכינו תבנית. בהגדרות → ערוצים → תבניות, צרו תבנית בקטגוריית שיווק עם המשתנים שאתם צריכים ושלחו לאישור Meta. בדרך כלל דקות עד שעות.
  2. צרו מפתח API בהגדרות → מפתחות API והעתיקו אותו — הוא מוצג פעם אחת.
  3. חברו את הטופס. בטופס הלידים שלכם, או במודול HTTP של Make, הוסיפו קריאת POST אחת:
POST /api/v1/send
curl -X POST https://quick-chat.app/api/v1/send \
  -H "Authorization: Bearer qsk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "{{ lead.phone }}",
    "contactName": "{{ lead.name }}",
    "template": {
      "name": "your_template_name",
      "language": "he",
      "variables": ["{{ lead.name }}"]
    },
    "idempotencyKey": "lead-{{ lead.id }}"
  }'
  1. תייגו את הליד (אופציונלי) בקריאה שנייה ל-/api/v1/contacts/tag, כדי שהוא יופיע בפילטר של ה-Inbox לפי הקמפיין שממנו הגיע.
  2. זהו. כשהלקוח יענה, התשובה תיכנס ל-Inbox כשיחה רגילה, החלון ייפתח, והצוות יוכל להמשיך משם בטקסט חופשי בלי עלות נוספת.

שאלה שלא מכוסה כאן?

כתבו לנו ל-support@quick-chat.app. אם אתם עדיין מתלבטים בין חיבור רשמי ל-Coexistence, המדריך WhatsApp Business API מסביר את ההבדל.