تخطّي إلى المحتوى
English كل صفحات التوثيق تسجيل الدخول

واجهة ربط القنوات

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

إن كنت مضيفًا تريد ربط قناة، فهذه ليست صفحتك — انظر اختيار المسار.

لا يحمل سويتي أي كود خاص بقناة بعينها. كل ما في هذه الصفحة واحد لكل منصّة، وهذا بالضبط ما يتيح لك الربط دون انتظار أن نبني لك شيئًا.

ما تحتاجه

اطلب منا تطبيقًا. نُنشئه ونرسل لك أربعة أشياء: معرّف عميل، وسرًّا (مرة واحدة — يُخزَّن مجزَّأً ولا يمكن عرضه مرة أخرى)، وعنوان إعادة التوجيه الذي سنقبله، ورابط موافقة لمضيف تجريبي حسابه يحتوي أصلًا على وحدات نموذجية وتقويم مسعّر وإقامات.

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

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

شكل التكامل

  1. يضغط مضيف على منصّتك اربط سويتي. ترسله إلى /channel/authorize.
  2. يسجّل الدخول، ويختار الوحدات التي يمكنك رؤيتها، ويوافق. يعود متصفحه إليك برمز.
  3. تستبدل الرمز برمز وصول ورمز تحديث.
  4. GET /units ← لكل وحدة GET /units/{unit}/listing ← ابنِ إعلانك ← أخبرنا بمعرّفه عبر PUT /units/{unit}/listing-link.
  5. GET /units/{unit}/calendar?from=&to= ← املأ إتاحتك وأسعارك والحد الأدنى للإقامة.
  6. بعد ذلك نرسل لك حدثًا موقّعًا كلما تغيّر شيء، فتعيد قراءة النقطة التي يشير إليها الحدث.
  7. يحجز ضيف على منصّتك ← POST /bookings. يرى المضيف الإقامة في تقويم سويتي خلال الطلب نفسه، وتُغلق الليلة على كل قناة أخرى يبيع من خلالها.

كل ما تتيحه هذه الواجهة

كامل السطح في جدول واحد، لترى ما تتكامل معه قبل أن تبدأ. كل سطر مرتبط بصلاحية يمنحها المضيف على حدة ويستطيع رفضها، فابنِ تكاملك على أنك قد تملك بعضها دون بعض.

ما تريد فعله كيف الصلاحية
الإعلانات
رؤية الوحدات التي منحك إياها المضيف GET /units units:read
قراءة وثيقة إعلان الوحدة كاملة — الأسماء والوصف والسعة والأسرّة والمرافق والعنوان والصور والرخصة والسعر GET /units/{unit}/listing units:read
تسجيل أنك أدرجت الوحدة، وبأي معرّف PUT /units/{unit}/listing-link listings:write
اقتراح بيانات وحدة لمراجعة المضيف POST /units/import units:write
الأسعار والإتاحة
قراءة الإتاحة والسعر وقيود الإقامة لكل ليلة GET /units/{unit}/calendar calendar:read
تحديد الأسعار والحد الأدنى للإقامة وقيود الوصول والمغادرة والإغلاق PUT /units/{unit}/calendar calendar:write
الحجوزات
تسجيل إقامة حجزها ضيف لديك POST /bookings bookings:write
نقل أو إلغاء أو تأكيد مغادرة إحدى إقاماتك PATCH/cancel/checkout على /bookings/{id} bookings:write
مطابقة إقاماتك مع ما لدينا GET /bookings?updated_since= bookings:read
محادثة الضيف
وضع رسائل ضيوفك أمام المضيف POST /messages messages:write
استقبال ردود المضيف حدث message.sent messages:read
تعويض ما فاتك بعد انقطاع المستقبِل GET /messages?since= messages:read
التشغيل
السؤال عمّا إذا كانت الوحدة جاهزة لضيفك GET /bookings/{id}/turnover operations:read
رؤية التنظيف والصيانة المحجوزين على وحدة GET /units/{unit}/cleanings و/maintenance operations:read
معرفة ما يمكن طلبه وكم يكلّف هذا المضيف GET /services?unit_id= operations:read
الإبلاغ عن عطل وجده ضيفك POST /units/{unit}/maintenance maintenance:write
طلب زيارة تنظيف POST /units/{unit}/cleanings cleaning:write
إلغاء تنظيف طلبته أنت POST /cleanings/{id}/cancel cleaning:write
أمور عامة
معرفة من هو متصل بك، وفصل الاتصال GET/DELETE /connection وGET /connections units:read / بلا
معرفة مصير طلب كتابة أرسلته GET /events/{id} bookings:read
الحصول على محتوى حقيقي لكل حدث نرسله GET /webhooks/samples وPOST /webhooks/test بلا

ما لا يُتاح عمدًا، ولماذا

قائمة كاملة حتى لا تبحث عنها. كل بند هنا شيء تراه هذه الواجهة ولا تسلّمه.

غير متاح لماذا
حجوزات القنوات الأخرى على الوحدة نفسها تظهر ليالي مغلقة فقط. الإقامة التي أخذها المضيف على Airbnb ضيفها لـ Airbnb لا لك — وما تحتاجه هو ألّا تكون الليلة قابلة للبيع، وهذا حاصل.
أسعار القنوات الأخرى للسبب نفسه، وهي تجاريًا ملك غيرك.
بريد الضيف أو هاتفه في حجز لم تنشئه أنت يعود إليك ما أرسلته أنت من بيانات اتصال، لا أكثر.
عامل النظافة: الاسم والهاتف والصورة والشركة لا علاقة بينك وبين من ينظّف الوحدة، وهويته ليست ملكنا لعرضها على شاشة يراها ضيف.
فوترة المضيف مع سويتي — المحفظة والباقات والفواتير والكشوف والعمولات شأن بينه وبيننا. ترى سعر زيارة توشك على طلبها وسعر زيارة طلبتها، لا غير.
صور إثبات التنظيف والملاحظات الداخلية للمشغّل صور من داخل مسكن شخص، وملاحظات خاصة عنه.
تطبيق محتوى الوحدة مباشرة POST /units/import ينشئ مسودّة يقبلها المضيف أو يتجاهلها. تعديل عنوان أو صور أحدهم بصمت تغييرٌ لم يوافق عليه أحد.
رموز الدخول والأقفال الذكية لم تُبنَ بعد — وليست محجوبة. حين توجد ستكون هنا.
وحدات المضيف الأخرى ترى ما أشّر عليه. وإعادة الموافقة هي طريقة تغيير ذلك.

ثلاثة أمور ستكلّفك يومًا إن لم تنتبه لها

كل واحد منها قرار يخمّنه المهندس المعقول عكسَ ما هو عليه.

الليلة غير المسعّرة تُنشر مغلقة

rate: null يأتي دائمًا مع available: false و reason: "unpriced". لم يقل أحد كم تكلّف تلك الليلة، فلا يوجد سعر صادق تُباع به. لا ترجع إلى قيمة افتراضية من عندك — يكتشف المضيف ذلك عند التحويل المالي، ولا يمكن استرجاعه.

أحداثنا مؤشّرات لا قيم

availability.changed يقول أي ليالٍ من أي وحدة تغيّرت. ولا يقول أبدًا إلى ماذا تغيّرت. أعد قراءة التقويم للنطاقات المذكورة.

هذا مقصود. تُعاد محاولة الإرسال نحو يوم ونصف وبلا ترتيب مضمون، فحدث يحمل قيمة قد يصل بعد أحدث منه ويكتب سعرًا قديمًا فوق جديد على إعلانك الحيّ — بصمت. المؤشّر لا يستطيع ذلك: تطبيقه مرتين أو متأخرًا أو خارج الترتيب ينتهي إلى المكان نفسه.

كل حدث تقويم أو إعلان يحمل sync_seq، رقمًا لا يتناقص لكل اتصال. إن كنت تجمّع الأحداث، تجاهل أي حدث رقمه أقل من رقم سبق أن تصرّفت بناءً عليه.

الحجز المتداخل يُقبل ولا يُرفض

افتراضيًا، إذا أخبرتنا بإقامة على ليالٍ محجوزة، نسجّلها ونجيب 201 مع meta.warnings تسمّي الليالي. ويُعرض على المضيف تعارض بيع مزدوج ليحلّه.

لأنك غالبًا بعتَ الإقامة وأخذت مال الضيف. رفضها لن يلغي البيع — سيعني فقط أن المضيف لن يعرف حتى يصل طرفان إلى باب واحد.

إن كانت منصّتك تنادينا قبل التأكيد للضيف، أخبرنا وسنحوّل تطبيقك إلى الوضع الصارم. عندها تحصل على 409 NIGHTS_UNAVAILABLE بالليالي المتعارضة، وهي إجابة يمكنك التصرّف بناءً عليها.

المصادقة

بيانا اعتماد، لكل منهما دوره.

بيانات تطبيقك — معرّف العميل والسر عبر HTTP Basic — تصادق نقطة الرموز ونقطة الإلغاء و GET /connections و POST /webhooks/test. هذه أسئلة عن تطبيقك لا عن مضيف بعينه.

رمز وصول الاتصال كـ Authorization: Bearer … يصادق كل ما عدا ذلك. ويُترجم إلى مضيف واحد بالضبط وإلى الوحدات التي اختارها بالضبط.

لا يوجد مفتاح على مستوى الحساب، وهذا بالتصميم. موافقة المضيف هي الطريق الوحيد إلى بيانات المضيف، ويمكنه إنهاؤها بنقرة واحدة من لوحته.

رابط الموافقة

https://suitiee.com/channel/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=YOUR_REGISTERED_URI      ← يُطابق حرفيًا، لا بالبادئة
  &scope=units:read calendar:read bookings:read bookings:write listings:write
  &state=YOUR_CSRF_VALUE                 ← يُعاد إليك
  &code_challenge=BASE64URL(SHA256(verifier))
  &code_challenge_method=S256            ← مطلوب؛ `plain` مرفوض

يسجّل المضيف الدخول إلى حسابه في سويتي (أو مشغّله، للوحدات التي يديرها)، ويرى ما تطلبه بلغة واضحة، ويختار وحداته، ويوافق.

استبدال الرمز

POST /api/v1/channel/oauth/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=…&redirect_uri=…&code_verifier=…

تحصل على رمز وصول (ثلاثون يومًا)، ورمز تحديث، وconnection_id. احفظ connection_id مع حساب المضيف لديك: فهو ثابت عبر إعادة الموافقة وهو ما يسمّيه كل حدث.

التحديث

POST /api/v1/channel/oauth/token
Authorization: Basic base64(client_id:client_secret)

grant_type=refresh_token&refresh_token=…

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

الصلاحيات

تُعرض على المضيف بلغته وتُفرض على كل طلب مقابل الرمز وما يمنحه المضيف حاليًا معًا — والأضيق يفوز. المضيف الذي يعيد الموافقة بصلاحيات أقل يُطاع في طلبك التالي لا بعد ثلاثين يومًا.

الصلاحية ما تتيحه
units:read الاتصال والوحدات الممنوحة ووثيقة الإعلان لكل وحدة
calendar:read الإتاحة والسعر وقيود الإقامة لكل ليلة
bookings:read الحجوزات التي أنشأتها أنت. لا حجوزات قناة أخرى أبدًا
bookings:write إنشاء حجوزاتك وتعديلها وإلغاؤها وتأكيد مغادرتها
listings:write تسجيل وحذف ربط «هذه الوحدة معروضة لدينا»
units:write اقتراح بيانات وحدة لمراجعة المضيف. لا يطبّقها أبدًا
messages:read استقبال ردود المضيف على ضيوفك عبر حدث message.sent
messages:write إرسال رسائل ضيوفك إلى صندوق محادثات المضيف في سويتي
calendar:write تحديد الأسعار والحد الأدنى للإقامة وإغلاق الليالي
operations:read الاطلاع على التنظيف والصيانة في الوحدات الممنوحة، وعلى كتالوج الخدمات
maintenance:write الإبلاغ عن عطل في وحدة. لا يكلّف شيئًا
cleaning:write طلب زيارة تنظيف. هذا يخصم من رصيد المضيف

الاصطلاحات

حدود المعدل. 120 طلبًا في الدقيقة لكل اتصال؛ و10 في الدقيقة لكل عنوان على نقطة الرموز. الرد 429 يحمل Retry-After.

الويب هوك

نرسل إلى عنوان واحد تعطينا إياه. موقّع بـ HMAC-SHA256 على "{timestamp}.{rawBody}":

X-Suitiee-Event: availability.changed
X-Suitiee-Event-Id: 3f2504e0-…          ← احذف التكرار بناءً عليه
X-Suitiee-Timestamp: 1789231200          ← ارفض ما هو أقدم من نافذتك
X-Suitiee-Signature-Version: 2
X-Suitiee-Callback-Signature: sha256=…

تحقّق منه ثم أجب بأي 2xx. نحن لا نقرأ محتوى ردّك.

عند إخفاقك. نعيد المحاولة تسع مرات على مدى يوم ونصف تقريبًا. بعد 24 ساعة من الإخفاق المتواصل نوقف تدفّقك، ونراسل جهة الاتصال الهندسية لديك، ونحتجز كل شيء — لا يُفقد شيء. عند الاستئناف تخرج الأحداث المحتجزة الأقدم أولًا بمعرّفاتها الأصلية.

أثناء تدوير السر تستقبل أيضًا X-Suitiee-Callback-Signature-Previous، وهو المحتوى نفسه موقّعًا بالسر السابق، لمدة 24 ساعة — لتنشر السر الجديد وفق جدولك لا جدولنا.

الأحداث

الحدث متى
connection.authorized أتمّ مضيف الموافقة، أو أعادها بوحدات مختلفة
connection.units_changed غيّر المضيف الوحدات التي يمكنك رؤيتها
connection.revoked فصلك المضيف. رموزك ميتة بالفعل
listing.updated تغيّر شيء تعرضه القناة عن وحدة مرتبطة
listing.deleted حُذفت الوحدة، أو أوقف المضيف منحها
availability.changed فُتحت ليالٍ أو أُغلقت
rates.changed تغيّرت الأسعار أو قيود الإقامة
booking.modified نُقل أحد حجوزاتك داخل سويتي
booking.cancelled أُلغي أحد حجوزاتك أو انتهت صلاحيته داخل سويتي
booking.checked_out تأكّد تنظيف ما بعد المغادرة لأحد حجوزاتك
message.sent ردّ المضيف على أحد ضيوفك. أوصِل الرد إليه
message.thread_closed لم يعد أحد في سويتي ينتظر شيئًا على تلك المحادثة
turnover.updated تغيّرت حالة زيارة تنظيف. completed على تنظيف ما بعد المغادرة تعني أن الوحدة جاهزة
maintenance.updated تغيّرت حالة زيارة صيانة — بما فيها التي فتحتها أنت
test.ping لا يُرسل إلا من POST /webhooks/test

GET /webhooks/samples يعيد محتوى حقيقيًا كاملًا لكل واحد منها.

لا يوجد في أي حدث أبدًا: بريد الضيف أو هاتفه، ولا حجوزات قناة أخرى، ولا أسعار قناة أخرى، ولا رمز دخول، ولا اسم من ردّ من عندنا.

حدث واحد فقط يحمل القيمة نفسها لا مؤشّرًا إليها. message.sent يحمل نص الرسالة. وهذا ليس استثناءً للتسهيل: قاعدة المؤشّرات موجودة لمنع قيمة قديمة من الكتابة فوق قيمة أحدث، والرسالة لا نسخة لاحقة لها لتكون قديمة مقابلها — فهي تُضاف ولا تُعدّل، ولها معرّفها الخاص، وتطبيقها مرتين أو متأخرًا ينتهي إلى النتيجة نفسها. احذف التكرار بناءً على message.id وكفى.

تحديد الأسعار وإغلاق الليالي

قراءة التقويم صلاحيتها calendar:read، والكتابة عليه calendar:write، ويمنحهما المضيف كلًّا على حدة — فتسجيل الإقامات التي تبيعها ليس الصلاحية نفسها التي تُعيد تسعير مسكن أحدهم.

لا يوجد «سعر خاص بالقناة». أنت تكتب على تقويم المضيف نفسه، فالسعر الذي تحدّده هنا هو السعر الذي تُبلَّغ به كل القنوات الأخرى التي يبيع عبرها: Airbnb وبوكينج خلال دقيقة، وبقية المنصات المتصلة في الدفعة التالية.

curl -sX PUT "https://suitiee.com/api/v1/channel/units/$UNIT/calendar" \
  -H "Authorization: Bearer $ACCESS" \
  -H 'Content-Type: application/json' \
  -d '{
        "from": "2026-07-01",
        "to": "2026-07-31",
        "weekdays": [4, 5],
        "rate": "600.00",
        "min_stay": 2
      }'

أرسل نطاقات لا ليالي مفردة. from وto شاملان، وweekdays (1 = الاثنين … 7 = الأحد) تضيّقهما، فيصبح تسعير عطلات موسم كامل نداءً واحدًا. تسعون نداءً لتسعين ليلة هو تسعون كتابة هنا وتسعون حدثًا عند كل قناة أخرى يبيع عبرها المضيف — وسيتجاوز حدّ المعدل لديك قبل أن ينتهي.

الحقل الغائب يُترك كما هو، وnull يمسحه. رفع سعر عطلة نهاية الأسبوع لا يستلزم إعادة إرسال حد أدنى للإقامة لم تمسّه — وإن أعدت إرساله بحكم العادة فستكتب فوق ما حدّده المضيف بصمت. التعليمتان مكتوبتان بشكلين مختلفين عن قصد:

ما ترسله ما يحدث
"min_stay": 3 يصبح الحد الأدنى للإقامة 3
"min_stay": null يُحذف الحد الأدنى، ويُطبَّق الافتراضي للوحدة
(الحقل غائب) لا يتغيّر

الحقول القابلة للكتابة هي rate وmin_stay وmax_stay وclosed_to_arrival و closed_to_departure وblocked. والاستجابة هي التقويم مقروءًا من جديد للنطاق الذي كتبته، لترى ما يقوله الآن — بما في ذلك الليالي التي لم تفتحها كتابتك لأن حجزًا قائمًا يحتجزها.

blocked ليست طريقة تسجيل حجز. استخدمها لليالي تحتجزها لسبب لا نراه. الإغلاق لا يحمل ضيفًا ولا تواريخ للتعديل ولا إلغاءً، ومن يغلق بدلًا من POST /bookings يترك مضيفًا لا يعرف من في مسكنه، وإقامةً لا يمكن مطابقتها أبدًا.

لا يمكنك إعادة فتح ليلة تحتجزها إقامة غيرك. الإتاحة مشتقّة من الحجوزات الحقيقية، و blocked: false تسحب إغلاقًا من عندك أنت فقط.

التشغيل: هل الوحدة جاهزة، وكيف تُنجَز الأمور

بيع الليلة هو النصف السهل. وهذه هي الأسئلة التي تأتي بعده، ولا يُجيب عن أيٍّ منها نظام حجوزات وحده.

«هل يمكننا تسجيل الدخول مبكرًا؟»

curl -s "https://suitiee.com/api/v1/channel/bookings/GTH-88213/turnover" \
  -H "Authorization: Bearer $ACCESS"

status: "completed" تعني أن الضيف السابق غادر وأن الوحدة نُظّفت واعتُمدت. وأي حالة أخرى تعني أن ذلك لم يحدث بعد، وwindow.end هو الجواب الصادق عن موعده. أما 404 TURNOVER_NOT_FOUND فتعني أنه لم يُجدوَل شيء بعد — فالحجز الذي تنتهي إقامته الأسبوع القادم لا تنظيف مجدول له، وهذه حقيقة لا خطأ.

ويمكن أن تُبلَّغ بدل أن تسأل: اشترك في حدث turnover.updated.

«المكيّف معطّل»

curl -sX POST "https://suitiee.com/api/v1/channel/units/$UNIT/maintenance" \
  -H "Authorization: Bearer $ACCESS" \
  -H 'Content-Type: application/json' \
  -d '{
        "trade": "ac",
        "priority": "urgent",
        "issue": "الضيف يفيد بأن مكيّف غرفة النوم الثانية يخرج هواءً دافئًا.",
        "booking_id": "GTH-88213"
      }'

لا يكلّف شيئًا ولا يلزم أحدًا بشيء: تُسجَّل الزيارة وتُسنَد إلى حرفة وتُسعَّر قبل أي مبلغ — ولهذا تكفيها صلاحية maintenance:write دون الصلاحية التي تخصم من رصيد المضيف.

trade واحدة من ac وplumbing وelectrical وappliance وgeneral. وpriority واحدة من low وnormal وhigh وurgent، وهي التي تحدّد نافذة جدولة الزيارة — فهي حقل حقيقي لا وسم. أرسل urgent لانقطاع الماء أو الكهرباء أو باب لا يُقفل. ومن يرسل كل شيء على أنه عاجل يحصل على مضيف لم يعد يصدّق أيًّا منه.

واكتب وصف العطل بكلمات الضيف نفسها ما أمكن. «مكيّف غرفة النوم الثانية يخرج هواءً دافئًا» يُصلَح من أول زيارة؛ و«معطّل» لا.

«هل يمكن تنظيفها أثناء الإقامة؟»

أولًا اعرف ما يمكن طلبه وكم يكلّف هذا المضيف:

curl -s "https://suitiee.com/api/v1/channel/services?unit_id=$UNIT&trade=cleaning" \
  -H "Authorization: Bearer $ACCESS"

يعود السعر محسوبًا عبر باقة المضيف وأي خصم تفاوضت عليه شركة الإدارة — لا سعر قائمة. اعرض على الضيف الرقم الذي سيُخصم من المضيف فعلًا، وإلا اكتشف المضيف الفرق في كشف حساب. وبدون unit_id يعود السعر null لا تخمينًا: الاستوديو والفيلا بأربع غرف ليسا تنظيفًا واحدًا.

curl -sX POST "https://suitiee.com/api/v1/channel/units/$UNIT/cleanings" \
  -H "Authorization: Bearer $ACCESS" \
  -H "Idempotency-Key: clean-40021" \
  -H 'Content-Type: application/json' \
  -d '{
        "service_id": "2c4e1a90-…",
        "window_start": "2026-10-07T11:00:00+03:00",
        "window_end":   "2026-10-07T15:00:00+03:00",
        "notes": "الضيف يفيد بأن المطبخ يحتاج عناية إضافية."
      }'

هذا يخصم من رصيد المضيف. تُحتسب الزيارة على باقته أو محفظته لحظة إنشائها، ولهذا لها صلاحية cleaning:write مستقلة، ولهذا يستطيع المضيف أن يمنحك كل شيء آخر ويرفض هذه وحدها. وأرسل Idempotency-Key: إعادة المحاولة بعد انقطاع يجب ألّا تُحتسب زيارة ثانية.

وأرسل نافذة يمكن إرسال فريق خلالها. «بين 11:00 و15:00» قابلة للجدولة؛ أما دقيقة محدّدة فوعد لا يستطيع أحد الوفاء به في مدينة فيها ازدحام.

و422 INSUFFICIENT_BALANCE تعني ما تقوله بالضبط — لا زيارة متبقية في الباقة ولا رصيد كافٍ في المحفظة. لم يُنشأ شيء ولم يُخصم شيء. وهذه رسالة للمضيف لا للضيف.

وPOST /cleanings/{id}/cancel تلغي زيارة طلبتها أنت. أما زيارة رتّبها المضيف أو مشغّله فليست لك لإلغائها وتعود 404. ويُعاد المبلغ للمضيف ويُصرَف الفريق، لكن ليس داخل مهلة الأربع والعشرين ساعة ولا بعد أن يصبح أحد داخل الوحدة — وهاتان تعودان 409 تسمّي القاعدة.

رؤية الأعمال على وحدة

GET /units/{unit}/cleanings وGET /units/{unit}/maintenance تعرضان كل زيارة على الوحدة، أيًّا كان من حجزها. وهذا مقصود: الوحدة التي تُنظَّف وحدة لا يمكن عرضها، والوحدة التي عليها زيارة صيانة عاجلة مفتوحة وحدة يجب التوقف عن بيعها قبل أن يصلها ضيف. وما لا تراه فيها هو من يقوم بالعمل، وكم يدفع المضيف، وأي إقامة باعتها قناة أخرى — فحقل booking_id يُملأ فقط حين تكون الإقامة إحدى إقاماتك.

رسائل الضيوف

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

ضيف يكتب إليك.

curl -sX POST https://suitiee.com/api/v1/channel/messages \
  -H "Authorization: Bearer $ACCESS" \
  -H "Idempotency-Key: msg-40021" \
  -H 'Content-Type: application/json' \
  -d '{
        "thread_id": "THREAD-9912",
        "message_id": "MSG-40021",
        "booking_id": "GTH-88213",
        "guest_name": "Sara Ali",
        "body": "متى يمكننا تسجيل الدخول؟",
        "sent_at": "2026-10-03T18:22:00+03:00"
      }'

201 في المرة الأولى، و200 إن كنا نحمل message_id نفسه — فإعادة إرسال صندوق صادرك بعد انقطاع آمنة وتخبرك بما كان لدينا. أرسل booking_id إن كان لديك، فترتبط المحادثة بالإقامة وتواريخها وبيانات الضيف؛ وأرسل unit_id إن لم يكن لديك؛ ولا ترسل أيًّا منهما وستظهر الرسالة على أي حال، وترتبط لاحقًا حين تحمل رسالة تالية رقم الحجز.

لا يوجد حقل sender. هذه النقطة تسجّل ما قاله الضيف. وإن كانت محادثتك تحوي ردود المضيف أيضًا فلا تعدها إلينا — فقد استلمتها منّا، ونسخة ثانية ستظهر تحت كلام المشغّل نفسه.

المضيف يردّ. يصلك الرد حدثًا باسم message.sent:

{
  "event": "message.sent",
  "event_id": "3f2504e0-…",
  "data": {
    "connection_id": "4f0d0a02-…",
    "thread_id": "THREAD-9912",
    "booking_id": "GTH-88213",
    "message": {
      "id": "c7a1e5b4-…",
      "body": "تسجيل الدخول من الساعة 15:00، ورمز الباب في بريد الوصول.",
      "sent_at": "2026-10-03T18:40:00+03:00"
    }
  }
}

إن كان مستقبِلك متوقفًا، اقرأ النافذة التي فاتتك بدل انتظار جدول إعادة المحاولة:

curl -s "https://suitiee.com/api/v1/channel/messages?thread_id=THREAD-9912&since=2026-10-03T00:00:00%2B03:00" \
  -H "Authorization: Bearer $ACCESS"

يعود الاتجاهان معًا، الأحدث أولًا: رسائلك بمعرّفاتك، وردود المضيف بمعرّفاتنا. والرد الذي ما زال في طريقه إليك يحمل "status": "pending"، لتفرّق بين «لم نرسلها» و«لم تستلمها».

لا بد أن يكون المضيف قد منح messages:read لتستقبل الردود أصلًا. وإن لم يفعل، فصندوق الرد في تلك المحادثة يخبره بذلك صراحةً بدل الإرسال إلى فراغ — ومن الأفضل طلب صلاحيتَي الرسائل معًا على شاشة الموافقة إن كنت تحمل محادثات الضيوف.

ما ليس بديهيًا

حجوزات القنوات الأخرى غير مرئية لك. إقامة أخذها المضيف على Airbnb، أو أدخلها بنفسه، لا تظهر في GET /bookings ولن تظهر. تظهر في التقويم كليلة مغلقة، وهذا كل ما تحتاجه لتتوقف عن بيعها.

الحجز حجزك بمعرّفك أنت. أرسل GTH-88213؛ واطلب GTH-88213. ما نخزّنه داخليًا شأننا.

التعديل يتوقف عند تأكيد التنظيف. بعد مغادرة الضيف وتسعير التنظيف واحتسابه وإسناده لفريق، لا يعود الحجز قابلًا للتعديل — ألغِه بدلًا من ذلك. ويقول ذلك 409 BOOKING_NOT_MODIFIABLE.

POST /bookings/{id}/checkout اختياري. يؤكّد سويتي التنظيف بنفسه في وقت المغادرة المتعاقد عليه. أرسله فقط حين تعرف فعلًا أن الضيف غادر: فذلك يوصل العامل قبل ساعات.

الحجوزات المؤقتة تشغل الليالي. status: "pending" يغلق الليالي تمامًا كإقامة مؤكدة — وأي شيء آخر يعني بيع الوحدة مرتين — ويُحرَّر تلقائيًا إن لم تؤكّده خلال نافذة تطبيقك (ستون دقيقة افتراضيًا).

التفعيل المباشر

نضع علامة على قائمة اعتماد كلما أثبتّ سلوكًا على البيئة التجريبية:

عند اكتمالها نحوّل تطبيقك إلى المباشر. المعرّف نفسه، والسر نفسه، والعناوين نفسها.

قائمة الاعتماد تغطي مسار البيع فقط. أما رسائل الضيوف والكتابة على التقويم ونقاط التشغيل فاختيارية — فالسوق الذي يبيع الليالي فقط تكاملٌ كامل ويُعتمد دون أن يمسّها. ولا يصبح أي بند منها إلزاميًا لمجرد وجود الإمكانية.

ما يتغيّر عند التفعيل المباشر هو أن الأمور تصبح حقيقية: تُحتسب زيارات التنظيف على المضيف وتُرسَل إلى فريق فعلي، وتصل بلاغات الصيانة إلى حرفي فعلي، ويتوقف وسم إرسالات الويب هوك لديك بأنها تجريبية. وفي البيئة التجريبية لا يخرج أيٌّ من ذلك من عندنا، وهذا ما يجعل تجربتها آمنة.

النداءات من البداية إلى النهاية

كل ما يلي طلب حقيقي. $TOKEN رمز وصول اتصال، و$CID/$SECRET بيانات اعتماد تطبيقك.

الوحدات التي منحك إياها المضيف

curl https://suitiee.com/api/v1/channel/units \
  -H "Authorization: Bearer $TOKEN"
{ "data": [ { "unit_id": "9f2c…", "name": {"en": "Olaya Suite", "ar": "جناح العليا"},
              "city": "riyadh", "currency": "SAR", "listing": null } ],
  "meta": { "current_page": 1, "last_page": 1, "per_page": 20, "total": 1 } }

وثيقة إعلانها — الأسماء والوصف والسعة والأسرّة والمرافق برموزها المعيارية والعنوان وروابط الصور وأوقات الدخول والرخصة والسعر المعلن، وhash:

curl https://suitiee.com/api/v1/channel/units/9f2c…/listing -H "Authorization: Bearer $TOKEN"

تقويمها

curl "https://suitiee.com/api/v1/channel/units/9f2c…/calendar?from=2026-10-01&to=2026-10-07" \
  -H "Authorization: Bearer $TOKEN"
{ "data": { "unit_id": "9f2c…", "currency": "SAR", "rate_plan": {"id": "standard"},
            "calendar_version": 1842,
            "nights": [
              {"date":"2026-10-01","available":true,"rate":"450.00","min_stay":2,
               "max_stay":null,"closed_to_arrival":false,"closed_to_departure":false,"reason":null},
              {"date":"2026-10-02","available":false,"rate":"450.00","min_stay":2,
               "max_stay":null,"closed_to_arrival":false,"closed_to_departure":false,"reason":"booked"}
            ] } }

سجّل معرّف إعلانك لنستطيع إخبارك حين تتغيّر تلك الوحدة:

curl -X PUT https://suitiee.com/api/v1/channel/units/9f2c…/listing-link \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"external_listing_id":"L-88213","url":"https://example.com/units/88213"}'

أرسل حجزًا

curl -X POST https://suitiee.com/api/v1/channel/bookings \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: gth-88213-create' \
  -d '{
    "booking_id": "GTH-88213",
    "unit_id": "9f2c…",
    "status": "confirmed",
    "checkin":  "2026-10-04T15:00:00+03:00",
    "checkout": "2026-10-07T11:00:00+03:00",
    "guest": {"name":"Sara Ali","email":"sara@example.com","phone":"+966500000000","language":"ar"},
    "amount": {"value":"1350.00","currency":"SAR"}
  }'

يعيد 201 مع الحجز، أو 201 مع meta.warnings إن كانت الليالي محجوزة. أرسل external_listing_id بدل unit_id بعد تسجيل الربط. وإعادة المحاولة بنفس Idempotency-Key تعيد الاستجابة نفسها لا إقامة ثانية.

عدّله أو ألغِه أو أكّد مغادرة الضيف

curl -X PATCH  …/bookings/GTH-88213          -d '{"checkout":"2026-10-09T11:00:00+03:00"}'
curl -X POST   …/bookings/GTH-88213/cancel
curl -X POST   …/bookings/GTH-88213/checkout -d '{"checkout_at":"2026-10-07T09:30:00+03:00"}'

اعرف ماذا فعلنا بما أرسلته

curl https://suitiee.com/api/v1/channel/events/gth-88213-create -H "Authorization: Bearer $TOKEN"

أثبت جاهزية مستقبِلك قبل أن يربطك أي مضيف:

curl -X POST https://suitiee.com/api/v1/channel/webhooks/test -u "$CID:$SECRET" -d event=test.ping
curl https://suitiee.com/api/v1/channel/webhooks/samples -H "Authorization: Bearer $TOKEN"

المطابقة الليلية

curl https://suitiee.com/api/v1/channel/connections -u "$CID:$SECRET"
curl "https://suitiee.com/api/v1/channel/bookings?updated_since=2026-10-01T00:00:00%2B03:00" \
  -H "Authorization: Bearer $TOKEN"

إرسال بيانات وحدة إلينا

إن كنت تملك أصلًا إعلانًا مكتملًا — بنى المضيف صفحته على موقعك أولًا ثم جاء ليربط — يمكنك عرضه على سويتي عبر POST /units/import، بالشكل نفسه الذي يعيده GET /units/{unit}/listing.

لا يتغيّر شيء بهذا النداء. يصبح مسودة يراجعها المضيف حقلًا حقلًا، والتطبيق فعله هو. وهذا مقصود: سجل الوحدة عند المضيف هو ما يحدّد ما يُقال للعامل عند الباب، وما تُعرض به على كل قناة أخرى، وما يُقدَّم للجهة التنظيمية كرقم رخصة — فكتابة سوق واحد عليه مباشرة تنشر خطأ إدخال واحدًا في كل مكان دون ما يدلّ على مصدره.

شكلان:

المفاتيح غير المعروفة تُتجاهل ولا تُرفض، فمحتواك الأغنى مقبول كما هو. تابع GET /events/{your-idempotency-key} لمعرفة مصير المسودة.

حين يحدث خطأ

401 UNAUTHENTICATED — رمز الوصول مفقود أو منتهٍ أو ملغى. جدّده. وإن أخفق التجديد أيضًا فقد فصلك المضيف وعليك حذف نسختك من بيانات اعتماده.

403 CONNECTION_REVOKED — الرمز سليم والمضيف أنهى العلاقة. إعادة المحاولة لن تفيد أبدًا. توقّف عن بيع وحداته واحذفها من منصّتك.

403 INSUFFICIENT_SCOPE — يسمّي details.required_scope الصلاحية الناقصة. منح المضيف أقل مما طلبت، أو ضيّقها لاحقًا. أعد إرساله إلى رابط الموافقة طالبًا ما تحتاجه.

404 UNIT_NOT_GRANTED — الوحدة ليست لك. إمّا أن المضيف ألغى تحديدها أو المعرّف خاطئ. أعد قراءة GET /units؛ وما ليس في تلك القائمة غير مرئي لك وإعادة السؤال ستظلّ تجيب 404.

409 LISTING_NOT_LINKED — أرسلت external_listing_id لإعلان لم يربطه أحد. أرسل PUT /units/{unit}/listing-link أولًا، أو أرسل unit_id بدلًا منه.

409 IDEMPOTENCY_MISMATCH — أعدت استخدام مفتاح بمحتوى مختلف. هذا خلل لديك: إمّا أن مولّد المفاتيح يكرّر، أو أن إعادة المحاولة غيّرت المحتوى. لا تتحايل بتوليد مفتاح جديد لإعادة المحاولة — اعرف أي المحتويين قصدت.

409 BOOKING_NOT_MODIFIABLE — تأكّد التنظيف وسُعِّر وأُسند. ألغِ وأعد الحجز.

429 — تجاوزت 120 طلبًا في الدقيقة على اتصال واحد. احترم Retry-After. وإن كنت تصطدم به أثناء مطابقة ليلية، استخدم GET /bookings?updated_since= بدل المرور على كل حجز.

توقّفت أحداثك. تحقّق مما إذا كانت نقطتك تجيب 2xx. بعد 24 ساعة من الإخفاق المتواصل نوقف تدفّقك ونراسل جهة الاتصال الهندسية لديك؛ لا يُفقد شيء، ويخرج كل المحتجز عند الاستئناف. اطلب منّا استئنافه.

تظن أنك فوّتّ حدثًا. غالبًا لم تكن بحاجة إليه: اسحب GET /units/{unit}/calendar وستحصل على الحقيقة الحالية على أي حال. وإن أردت اليقين، فنحن نعيد إعلان الأفق الكامل لكل إعلان مرة كل ليلة، فأي اختلاف يصحّح نفسه خلال يوم.

حجز أرسلته ليس في تقويم المضيف. ابحث عنه بـ GET /events/{your-idempotency-key} — يقول ما فعلناه بالطلب ولماذا رفضناه إن رفضناه.

422 BEYOND_HORIZON عند الكتابة على التقويم. أنت تكتب أبعد مما نحتفظ بتقاويم له. و details.max_date هي أبعد ليلة يمكنك ضبطها؛ اطلب منّا توسيع الأفق لتطبيقك إن كنت تسعّر أبعد من ذلك فعلًا.

422 INSUFFICIENT_BALANCE عند طلب تنظيف. لا زيارة متبقية في باقة المضيف ولا رصيد كافٍ في محفظته. لم يُنشأ شيء ولم يُخصم شيء. وهذه رسالة للمضيف لا للضيف — فالضيف لا يستطيع معالجتها.

422 SERVICE_UNAVAILABLE عند طلب تنظيف. المعرّف service_id ليس صفًّا فعّالًا في الكتالوج. أعد قراءة GET /services؛ فالأكواد والمعرّفات تتغيّر حين يتغيّر الكتالوج.

404 CLEANING_NOT_FOUND عند الإلغاء. إمّا أن المعرّف خاطئ، وإمّا أن الزيارة لم تطلبها أنت. الزيارة التي رتّبها المضيف أو مشغّله ليست لك لإلغائها، ونجيب بـ 404 لا 403 حتى لا يخبرك معرّف مُخمَّن بشيء.

409 CANCEL_TOO_LATE وCANCEL_BLOCKED. الزيارة داخل مهلة الأربع والعشرين ساعة، أو أن فريقًا صار داخل الوحدة. ولا تُعاد المحاولة في أيٍّ منهما؛ يتولّى الأمر شخص مع المضيف.

صندوق الرد في محادثة يقول إن المضيف لا يستطيع الرد عليك. إمّا أنه لم يمنح messages:read، وإمّا أنه ليس لديك مستقبِل ويب هوك مشترك في message.sent. وكلاهما بيدك: اطلب الصلاحية عند الموافقة، واشترك في الحدث.

روابط التقويم ما زالت موجودة

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

آخر تحديث 2026-09-09

هل أفادتك هذه الصفحة؟