Skip to content

שגיאות וסטטוסים

TextMe מדווחת על כשלים בגוף של תשובת HTTP 200 רגילה לחלוטין. יש מבנה אחד לכולם, ושדה אחד שעליו מתנים את הלוגיקה.

מעטפת השגיאה

json
{
  "status": 4,
  "message": "Not enough credit"
}
שדהמשמעות
status0 בהצלחה; כל ערך אחר הוא כשל.
messageהסבר קריא לאדם. בסטטוס 2 הוא מציין את השדה החסר.

אף פעם לא להתנות על סטטוס ה-HTTP לבדו

כשלי אימות, כשלי ולידציה וכשלי מכסה — כולם מגיעים כ-HTTP 200. לקוח שבודק רק את response.ok יתייחס לכל אחד מהם כאל שליחה מוצלחת.

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

שני סטטוסים, שני אוצרות מילים

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

איפהאוצר המילים
סטטוס הבקשהstatus ברמה העליונה בכל תשובההאם ה-API קיבל את הקריאה?קודי סטטוס
סטטוס המסירהstatus בתוך כל רשומה ב-transactions[] של דוח מסירהמה קרה להודעה במכשיר?סטטוסי מסירה

דוח מסירה יכול להחזיר status: 0 ברמה העליונה (הדוח הופק) בעוד שרשומות בודדות בתוכו נושאות 102 (נמסר) או 1 (נכשל). מדובר בשני סולמות שונים שבמקרה חולקים שם שדה.

הצלחה חלקית

כמה פעולות מקבלות אצווה ומעבדות אותה שורה-שורה. הן מחזירות status: 0 — הפעולה רצה — יחד עם רשימת errors שמתארת את השורות שלא נכנסו.

json
{
  "status": 0,
  "message": "The phone numbers have been added successfully",
  "errors": [
    "The phone 05XXXXXXXX is already on the contact list and therefore not added"
  ]
}

הפעולות שמתנהגות כך הן כתיבות לרשימות תפוצה (newCL, removeCL, addNumCL, rmNumCL). התייחסות ל-status: 0 שלהן כאל "הכול עבד" תשמיט נמענים בשקט — קראו את errors בכל פעם שהוא קיים.

סוגי כשלים

פרטי גישה והרשאות3, 10, 11, 504, 511. הטוקן שגוי, פג תוקף, לא תואם, או שהחשבון אינו רשאי לבצע את הפעולה. ניסיון חוזר לא יעזור; ראו אימות.

בקשה פגומה1, 2, 997. המסמך לא נפרסר, חסר שדה חובה, או שאלמנט השורש אינו פעולה. דטרמיניסטי: אותו גוף בקשה ייכשל תמיד. תקנו את הבקשה; נקודת הקצה לבדיקות היא הדרך הזולה לעשות זאת.

ערכים שנדחו9, 714, 980, 986, 989, 990, 991, 992, 993, 995, 996. שדה מסוים נכשל בוולידציה — מספר קצר מדי, הודעה ארוכה מדי, ערך add_unsubscribe שאינו 2 או 3. ה-message מציין איזה.

מצב החשבון4 (אין יתרה), 12 (אין מספיק כסף), 5 (אין הרשאת שליחה בשעה הזו), 515 (שולח לא מאומת). אין שום בעיה בבקשה; יש בעיה בחשבון. הקריאות האלה עשויות להצליח בהמשך, אחרי טעינת יתרה, בשעה אחרת, או לאחר אימות השולח.

לא נשאר למי לשלוח8 (כל היעדים חסומים), 715 (כל היעדים סוננו על ידי temp_bl), 988 (רשימת התפוצה אינה קיימת). הקריאה הייתה תקינה ופשוט לא נשארו נמענים. כדאי לתעד את אלה בנפרד: בדרך כלל הבעיה היא בקהל, לא בקוד.

בצד השרת6, 970, 999. כשל תהליך. אפשר לנסות שוב פעם אחת עם השהיה; אם זה חוזר, פנו לתמיכה.

ניסיונות חוזרים בבטחה

אין מנגנון idempotency key. שליחה שנשלחת פעמיים היא שתי שליחות, ושתיהן יימסרו וייגבו.

  • אף פעם לא לנסות שליחה מחדש בעיוורון. אם sms או bulk נכשלים בטיים-אאוט בשכבת התעבורה, אינכם יודעים אם ההודעה יצאה. פתרו זאת בקריאה ולא בכתיבה: תנו id לכל יעד (ראו דוחות מסירה) ובדקו בדוח לפני שאתם שולחים שוב.
  • קריאות בטוחות לחזרה. balance, dlr, dlrByDate, incoming, getCL, getVerifiedPhones ושאר פעולות הקריאה אינן משנות דבר.
  • כתיבות אינן אידמפוטנטיות. קריאה כפולה ל-newCL יוצרת שתי רשימות; addNumBL פעמיים אינה מזיקה, אבל updateAmountSub פעמיים מעבירה קרדיט פעמיים.
  • השהו לפני ניסיון חוזר ב-6 וב-999. נסו שוב לאחר השהיה קצרה ולא מיד.

דוגמה מלאה

התבנית שכל דוגמה באתר הזה משתמשת בה, במלואה:

js
const response = await fetch('https://my.textme.co.il/api', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TEXTME_API_TOKEN}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(payload),
})

// שכבת התעבורה נכשלת רק לעיתים רחוקות, אבל כשזה קורה אינכם יודעים אם
// ההודעה נשלחה — אין לנסות שליחה מחדש כאן.
if (!response.ok) {
  throw new Error(`TextMe transport error: HTTP ${response.status}`)
}

const result = await response.json()

// זו הבדיקה שקובעת.
if (Number(result.status) !== 0) {
  throw new TextMeError(result.status, result.message)
}

// פעולת אצווה יכולה להצליח בסך הכול ובכל זאת להשמיט שורות.
for (const problem of result.errors ?? []) {
  console.warn('TextMe partial failure:', problem)
}

return result
python
import os

import httpx


class TextMeError(RuntimeError):
    def __init__(self, status, message):
        super().__init__(f"TextMe {status}: {message}")
        self.status = int(status)
        self.message = message


def call(payload):
    response = httpx.post(
        "https://my.textme.co.il/api",
        headers={"Authorization": f"Bearer {os.environ['TEXTME_API_TOKEN']}"},
        json=payload,
    )
    # כשל תעבורה משאיר שליחה במצב לא ידוע — אין לנסות אותה מחדש.
    response.raise_for_status()

    result = response.json()

    # זו הבדיקה שקובעת.
    if int(result["status"]) != 0:
        raise TextMeError(result["status"], result["message"])

    # פעולת אצווה יכולה להצליח בסך הכול ובכל זאת להשמיט שורות.
    for problem in result.get("errors", []):
        print("TextMe partial failure:", problem)

    return result
php
<?php

function textme(array $payload): array
{
    $client = new \GuzzleHttp\Client([
        'headers' => [
            'Authorization' => 'Bearer '.getenv('TEXTME_API_TOKEN'),
            'Accept' => 'application/json',
        ],
    ]);

    $response = $client->post('https://my.textme.co.il/api', ['json' => $payload]);
    $result = json_decode($response->getBody()->getContents(), true);

    // זו הבדיקה שקובעת.
    if ((int) $result['status'] !== 0) {
        throw new RuntimeException("TextMe {$result['status']}: {$result['message']}");
    }

    // פעולת אצווה יכולה להצליח בסך הכול ובכל זאת להשמיט שורות.
    foreach ($result['errors'] ?? [] as $problem) {
        error_log("TextMe partial failure: {$problem}");
    }

    return $result;
}

הטבלאות המלאות

  • קודי סטטוס — כל הערכים ש-status ברמה העליונה יכול לקבל.
  • סטטוסי מסירה — כל הערכים שרשומת דוח מסירה יכולה לקבל, עם תרגום לאנגלית של ההודעות בעברית.