تخطَّ إلى المحتوى
المحتويات
بيانات مساحة العمل

جهات الاتصال

جهات الاتصال هم الأشخاص الذين تراسلهم مساحة العمل، مفتاحهم رقم الهاتف. والجزء المهم هو الاستيراد الجماعي: على أي مفتاح يزيل التكرار، وماذا يفعل حين يجد تطابقا.

النقاط

GET/v1/contactsPOST/v1/contactsGET/v1/contacts/{contactId}PATCH/v1/contacts/{contactId}DELETE/v1/contacts/{contactId}

جهة الاتصال فريدة في مساحة العمل برقمها بعد تطبيعه إلى E.164. وحذفها حذف ناعم، وإنشاء الرقم نفسه أو استيراده بعدها يستعيدها بدل أن يُنشئ صفا ثانيا.

curl
curl -X POST https://whats.azzamkh.sa/api/v1/contacts \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+9665XXXXXXXX",
    "name": "سارة الأحمد",
    "email": "sara@example.com",
    "locale": "ar",
    "opt_in_status": "opted_in",
    "tags": ["vip"],
    "attributes": { "customer_id": "A-10428" }
  }'

# 201 Created
# { "id": "cnt_...", "phone": "+9665XXXXXXXX", "source": "api", ... }

# The number already exists and is live:
# 409 { "error": { "code": "CONTACT_ALREADY_EXISTS",
#                  "details": { "contact_id": "cnt_..." } } }

الحقول

الحقلالنوعمطلوبالوصف
phonestringمطلوبالرقم. يُطبَّع إلى E.164 عند الكتابة، ولا يتغيّر بعدها.
namestringاختياريالاسم المعروض.
first_name, last_namestringاختياريالاسم الأول واسم العائلة. وإن لم يكن هناك اسم معروض رُكّب منهما.
emailstringاختيارييُحوَّل إلى حروف صغيرة عند الكتابة.
localestringاختياريرمز لغة مثل ar أو en-GB.
notesstringاختيارينص حر.
opt_in_status"unknown" | "opted_in" | "opted_out"اختياريموافقة التسويق. الافتراضي unknown.
attributesobject<string, string>اختياريبياناتك الخاصة كمفتاح وقيمة. المفاتيح بصيغة lower_snake_case؛ وعددها وطول القيمة كلاهما محدود.
tagsstring[]اختياريأسماء الوسوم لا معرّفاتها. والأسماء المجهولة تُنشأ.
group_idsstring[]اختياريمعرّفات المجموعات التي تُضاف إليها جهة الاتصال.
default_calling_codestringاختيارييُستخدم لتطبيع رقم مكتوب بالصيغة المحلية، مثل 966.

null يمسح، والغياب يترك

عند التعديل تفرّق الحقول النصية القابلة للإفراغ بين الأمرين: إرسال null يمسح العمود، وإغفال الحقل يتركه كما هو. وإرسال tags أو group_ids يستبدل المجموعة كلها لا يضيف إليها.

المرشّحات

المعاملالقيمالوصف
qstringبحث غير حساس للحالة في الاسم والبريد والرقم. والعبارة التي فيها ثلاثة أرقام فأكثر تطابق الرقم أيضا.
tagtag_… | slugمعرّف وسم عام أو الـ slug الخاص به — كلاهما يعمل.
group_idgrp_…جهات الاتصال في مجموعة واحدة.
opt_in_statusunknown, opted_in, opted_outالترشيح بالموافقة.
sourcemanual, import, api, inbound, campaign, automation, systemكيف دخلت جهة الاتصال مساحة العمل.
limit, afterتقسيم بالـ cursor، الأحدث أولا.

القيمة المجهولة في opt_in_status أو source تعطي 422 يسمّي المجموعة المسموحة، لا مرشّحا يُتجاهل.

المجموعات

GET/v1/contact-groupsPOST/v1/contact-groupsGET/v1/contact-groups/{groupId}PATCH/v1/contact-groups/{groupId}DELETE/v1/contact-groups/{groupId}GET/v1/contact-groups/{groupId}/membersPOST/v1/contact-groups/{groupId}/membersDELETE/v1/contact-groups/{groupId}/members

المجموعة قائمة ثابتة. تُحرَّر العضوية جماعيا، ونقطة الإزالة تأخذ جسما في طلب DELETE — وهو أمر غير معتاد، لكنه ما يتيح لك إزالة ألف جهة اتصال في استدعاء واحد.

الوسوم

GET/v1/tagsPOST/v1/tagsDELETE/v1/tags/{tagId}

للوسم اسم وslug مولَّد ولون اختياري. وجهات الاتصال تشير إلى الوسوم بالاسم عند الكتابة وتستقبلها كائنات عند القراءة.

استيراد جدول

POST/v1/contacts/importGET/v1/contacts/importsGET/v1/contacts/imports/{importId}

يصل الملف مرمّزا بـ base64 داخل JSON: لا توجد نقطة multipart. ويُقبل الاستيراد بـ 202 ويعمل في الخلفية، فاستعلم عن العملية لمعرفة التقدم والنتائج. وأرسل dry_run: true أولا — فهو يحلّل ويتحقق ويحصي دون أن يكتب شيئا، وهو السبيل الآمن الوحيد لمعرفة ماذا سيفعل الملف.

curl
# The file travels base64-encoded inside JSON. There is no multipart route.
CONTENT=$(base64 < contacts.csv | tr -d '\n')

curl -X POST https://whats.azzamkh.sa/api/v1/contacts/import \
  -H "Authorization: Bearer $WA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"filename\": \"contacts.csv\",
    \"format\": \"csv\",
    \"content\": \"$CONTENT\",
    \"dry_run\": true,
    \"default_calling_code\": \"966\"
  }"

# 202 Accepted — the import runs in the background. Poll it:
curl https://whats.azzamkh.sa/api/v1/contacts/imports/imp_... \
  -H "Authorization: Bearer $WA_API_KEY"

# {
#   "id": "imp_...",
#   "status": "completed",
#   "dry_run": true,
#   "progress": { "total_rows": 1200, "processed_rows": 1200, "fraction": 1 },
#   "result": { "created": 1140, "updated": 48, "duplicates": 52, "invalid": 8 },
#   "errors": [
#     { "row": 74, "column": "phone", "reason": "invalid_phone", "value": "05x1234" }
#   ]
# }

الحدود

الحدالقيمة
الصيغcsv, xlsx
حجم الملف بعد فك الترميز5 MiB
الصفوف في العملية الواحدة50 000
أخطاء الصفوف المسرودة100
مدة حفظ الملف7 d

تُكتشف الصيغة من بايتات الملف الأولى لا من امتداده، فالجدول المحفوظ بلاحقة خاطئة يُستورد صحيحا. والصيغة القديمة ‎.xls غير مدعومة.

إزالة التكرار

تحدث إزالة التكرار مرتين، على مفتاحين، وسلوكهما مختلف. وكلاهما يعتمد رقم الهاتف بعد تطبيعه.

  • داخل الملف الواحد تُدمج الأرقام المكررة لا تُسقَط: القيم غير الفارغة الأحدث تغلب، وتُوحَّد الوسوم، وتُدمج الخصائص. ويُحسب الصف مكررا لكن التعديل يُحفظ.
  • مقابل مساحة العمل، الرقم المطابق يُحدَّث ولا يُتخطى أبدا. فالاستيراد عملية upsert.
  • لا يكتب فوق القيمة إلا قيمة غير فارغة. فالعمود الفارغ لا يمحو بيانات عندك أصلا — وهذا ما يجعل استيراد جدول ناقص آمنا.
  • جهة اتصال محذوفة حذفا ناعما يظهر رقمها في الملف تُستعاد.

الأعداد الأربعة لا تُجمع — عمدا

total_rows = created + duplicates + invalid. أما updated فهو عدّ فرعي داخل duplicates لا فئة رابعة، فجمع الأربعة يحسب كل صف محدَّث مرتين.

ربط الأعمدة

تُطابق العناوين مع مرادفات إنجليزية وعربية، فملف فيه عمود عنوانه رقم الجوال يُربط بحقل الهاتف بلا إعداد. وأرسل mapping صريحا لتتجاوز ذلك. والعمود الذي لا يُربط بشيء يصير خاصية مخصصة بدل أن يُهمل.

reasonالوصف
missing_phoneالصف بلا رقم هاتف.
invalid_phoneتعذّر تطبيع الرقم إلى E.164.
duplicate_in_fileظهر الرقم قبل ذلك في الملف نفسه.
row_too_wideأعمدة الصف أكثر من أعمدة العنوان.
unmapped_headerتعذّر ربط عنوان وتعذّر جعله خاصية.