الانتقال إلى المحتوى

للمطوّرين

ابنِ على الشبكة

شغّل المهام على الشبكة العالمية، وتابع تقدّمها، واستلم نتائج تم التحقق منها من شيفرتك الخاصة — أو ابنِ تطبيقًا يفعل ذلك للآخرين. واجهة HTTPS واحدة لبرامجك الخاصة وللتطبيقات التي تعمل نيابةً عن الآخرين: مفتاح API (dk_…) لشيفرتك، ورمز وصول OAuth 2 (do_…) لتطبيق أذن له أحدهم، ونطاقات صلاحية تحدّ من كليهما، وخطافات ويب موقّعة بـ HMAC لتعلم بالأحداث لحظة وقوعها.

بيانات الاعتماد

  • dk_…مفتاح API — لبرامجك وخوادمك
  • do_…رمز وصول — ساعة واحدة
  • dr_…رمز تجديد — ثلاثون يومًا، استخدام واحد
  • whsec_…سرّ توقيع خطافات الويب

تُرسل جميعها في Authorization: Bearer …، باستثناء سرّ خطاف الويب الذي لا يغادر خادمك أبدًا.

البداية

طريقتان للدخول

استخدم مفتاحًا حين تكون الشيفرة لك وتعمل لحسابك أنت. واستخدم OAuth حين يسجّل الناس الدخول إلى تطبيقك ويسمحون له بالعمل نيابةً عنهم.

بمفتاح API

  1. 1

    أنشئ مفتاحًا في إعدادات المطوّر. امنحه اسمًا ونطاقات الصلاحية التي يحتاجها فقط، وتاريخ انتهاء إن كان لغرض مؤقت.

    إلى أن يتأكّد بريدك الإلكتروني، لا يحمل المفتاح إلا نطاقات القراءة.

  2. 2

    انسخ المفتاح حين يظهر. فهو لا يظهر إلا مرة واحدة: لا نحتفظ إلا ببصمة منه، فالمفتاح المفقود لا يُستعاد بل يُستبدل.

  3. 3

    أرسله رمزًا من نوع bearer مع كل طلب:

    shell
    curl https://api.thecubefloor.com/v1/me \
      -H "Authorization: Bearer dk_…"
  4. 4

    ألغِه من الصفحة نفسها حالما تنتهي الحاجة إليه. تُظهر القائمة آخر استخدام لكل مفتاح.

باستخدام OAuth 2

منح رمز التفويض، مع PKCE (S256) إلزاميًا في كل طلب. كل تطبيق عميل سرّي يملك سرًّا خاصًا به.

  1. 1

    سجّل التطبيق في إعدادات المطوّر: اسمه (يراه الناس حين يُطلب إذنهم)، وعناوين إعادة التوجيه، ونطاقات الصلاحية التي يحق له طلبها. تحصل على client_id وclient_secret، ويظهر السرّ مرة واحدة. يتطلّب تسجيل تطبيق بريدًا إلكترونيًا مؤكّدًا.

    يجب أن تطابق عناوين إعادة التوجيه حرفًا بحرف. وهي عناوين https:// كاملة بلا جزء بعد العلامة #؛ ولا يُقبل http:// العادي إلا نحو localhost أو عنوان الحلقة المحلية، للتطوير والتطبيقات الأصلية.

  2. 2

    أنشئ code_verifier عشوائيًا ووجّه متصفح الشخص إلى /oauth/authorize مع بصمته SHA-256 في code_challenge:

    shell
    # المُتحقِّق: من 43 إلى 128 حرفًا غير محجوز. احتفظ به حتى الاستبدال.
    CODE_VERIFIER=$(openssl rand -base64 64 | tr -dc 'A-Za-z0-9' | cut -c1-64)
    CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary \
      | openssl base64 -A | tr '+/' '-_' | tr -d '=')
    
    https://api.thecubefloor.com/oauth/authorize?response_type=code
      &client_id=app_…
      &redirect_uri=https%3A%2F%2Fyour.app%2Fcallback
      &scope=account%3Aread%20jobs%3Aread
      &state=…
      &code_challenge=$CODE_CHALLENGE
      &code_challenge_method=S256
  3. 3

    يرى صفحة الموافقة الخاصة بنا على thecubefloor.com، وفيها اسم تطبيقك وما يطلبه والمكان الذي سيعود إليه. إن وافق، يعود المتصفح إلى عنوان إعادة التوجيه ومعه code وstate الخاص بك؛ وإن رفض، يعود ومعه error=access_denied.

  4. 4

    استبدل الرمز بالرموز خلال دقيقة واحدة ولمرة واحدة فقط. صادِق عبر HTTP Basic أو بإرسال client_id وclient_secret في النموذج:

    shell
    curl https://api.thecubefloor.com/oauth/token \
      -u "$CLIENT_ID:$CLIENT_SECRET" \
      -d grant_type=authorization_code \
      -d code="$CODE" \
      -d redirect_uri=https://your.app/callback \
      -d code_verifier="$CODE_VERIFIER"
  5. 5

    يتضمّن الرد رمز وصول (do_…، صالح لساعة) ورمز تجديد (dr_…، ثلاثون يومًا). كل تجديد يستهلك رمز التجديد ويعيد زوجًا جديدًا؛ وتقديم رمز مستهلك يُنهي كل الرموز الناشئة عن الموافقة نفسها، لأن أحد حامليه لص.

    json · shell
    {
      "access_token": "do_…",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "dr_…",
      "scope": "account:read jobs:read"
    }
    
    curl https://api.thecubefloor.com/oauth/token \
      -u "$CLIENT_ID:$CLIENT_SECRET" \
      -d grant_type=refresh_token \
      -d refresh_token="$REFRESH_TOKEN"
  6. 6

    حين يقطع الشخص الاتصال في تطبيقك، أعِد الرمز (RFC 7009). رمز التجديد يُنهي الموافقة كلها:

    shell
    curl https://api.thecubefloor.com/oauth/revoke \
      -u "$CLIENT_ID:$CLIENT_SECRET" \
      -d token="$REFRESH_TOKEN"

حين يُرفض طلب

الأخطاء على الشكل {"error": "E_…", "message": "…"}: رمز يستطيع برنامجك التصرّف بناءً عليه وجملة يفهمها الإنسان. يعني 401 أن بيانات الاعتماد مفقودة أو مجهولة أو ملغاة أو منتهية؛ و403 E_SCOPE أنها لا تملك نطاق الصلاحية؛ و403 E_SESSION_ONLY أن العملية مخصّصة للموقع نفسه. أما نقاط /oauth/ فتجيب بصيغة RFC 6749.

نطاقات الصلاحية

ما يمكن لمفتاح أو رمز فعله

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

نطاقات الصلاحية ونقاط الوصول التي يفتحها كل منها
النطاقما يسمح بهنقاط الوصول
account:read

قراءة حسابك

بيانات حسابك وأرصدتك.

  • GET /v1/me
jobs:read

الاطلاع على مهامك

المهام التي شغّلتها وتقدّمها ونتائجها.

  • GET /v1/jobs
  • GET /v1/jobs/{id}
jobs:write

تشغيل المهام وإلغاؤها

بدء المهام وتقدير كلفتها وإلغاؤها، مع الإنفاق من رصيد حسابك.

  • POST /v1/jobs
  • POST /v1/jobs/estimate
  • POST /v1/jobs/{id}/cancel
machines:read

الاطلاع على أجهزتك

أجهزتك والأوامر المرسلة إليها وما حققته من أرباح.

  • GET /v1/me/machines
  • GET /v1/me/earnings
  • GET /v1/me/machines/{handle}/control
machines:write

التحكم في أجهزتك

إعادة تشغيل أجهزتك أو إيقافها أو تغيير عناوينها.

  • POST /v1/me/machines/{handle}/control
money:read

الاطلاع على عمليات شحن الرصيد والسحب

عمليات شحن الرصيد والسحب الخاصة بك، وحالة كل منها.

  • GET /v1/me/topups
  • GET /v1/me/withdrawals
drive:read

الاطلاع على التخزين المتصل

هل التخزين الخاص بك متصل، مع ملخص لمحتواه.

  • GET /v1/drive/google
  • GET /v1/storage/mine
webhooks:write

إدارة خطافات الويب (webhooks)

إضافة العناوين التي تُرسل إليها أحداثك واختبارها وحذفها.

  • /v1/me/webhooks …

خطافات الويب

اعلم بالحدث لحظة وقوعه

سجّل عنوان https:// والأحداث التي تريدها في إعدادات المطوّر. يُرسل كل حدث إليه بطلب POST بصيغة JSON، موقّعًا بالسرّ الذي ظهر عند إنشاء خطاف الويب.

الأحداث

أنواع الأحداث ومعنى كل منها
الحدثيُرسل حين
job.finishedانتهت مهمة ونتائجها جاهزة
job.failedفشلت مهمة
job.undeliveredشُغّلت مهمة لكن تعذّر تسليم نتائجها
topup.confirmedتأكّد شحن الرصيد
withdrawal.paidدُفع سحب
withdrawal.rejectedرُفض سحب
machine.claimedأصبحت أرباح جهاز تعود إلى هذا الحساب
pingحدث تجريبي يُرسل عند الضغط على «إرسال اختبار»

الترويسات

DAIN-Webhook-Id
معرّف الحدث، ولا يتغيّر في أي إعادة محاولة. استخدمه لتجاهل التكرار: يُسلَّم كل حدث مرة واحدة على الأقل.
DAIN-Webhook-Timestamp
ثواني Unix لهذه المحاولة. ارفض كل ما مضى عليه أكثر من خمس دقائق.
DAIN-Webhook-Signature
HMAC-SHA256 للطابع الزمني ثم نقطة ثم الجسم الخام، بمفتاح هو سرّك whsec_…، بالصيغة الست عشرية.
v1=<hex HMAC-SHA256(secret, timestamp + "." + body)>

ما يصلك

http
POST /your/webhook HTTP/1.1
Content-Type: application/json
DAIN-Webhook-Id: evt_…
DAIN-Webhook-Timestamp: 1793351642
DAIN-Webhook-Signature: v1=5f2b…c09e

{
  "id": "evt_…",
  "type": "job.finished",
  "created_at": "2026-10-30T09:14:02Z",
  "data": { "job": { "id": "job_…", "state": "done", … } }
}

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

إعادة المحاولة

  1. المحاولة 1فورًا
  2. المحاولة 2+1 د
  3. المحاولة 3+5 د
  4. المحاولة 4+30 د
  5. المحاولة 5+2 س
  6. المحاولة 6+6 س
  7. المحاولة 7+24 س
  8. معطّل

أي رد 2xx يُنهيها. وإلا تُعاد محاولة التسليم بعد كل مهلة من هذه المهل — سبع محاولات على مدى 33 ساعة تقريبًا — وإذا فشلت الأخيرة يُعطَّل خطاف الويب. وإرسال اختبار يحصل على رد 2xx يعيد تفعيله. لا تُتّبع عمليات إعادة التوجيه.

تُفحص العناوين بعد استعلام DNS، عند التسجيل ومع كل تسليم: لا يُستدعى أبدًا أي عنوان خاص أو حلقة محلية أو محلي الرابط أو محجوز.

التحقق من التوقيع

احسب التوقيع على الجسم كما وصل تمامًا — قبل أي تحليل لـ JSON — وقارن في زمن ثابت.

python
import hashlib, hmac, time

def verify(secret: str, headers, body: bytes) -> bool:
    """body: the request body exactly as it arrived, before json.loads."""
    ts = headers["DAIN-Webhook-Timestamp"]
    sig = headers.get("DAIN-Webhook-Signature", "")
    if abs(time.time() - int(ts)) > 300:  # more than five minutes old
        return False
    mac = hmac.new(secret.encode(), ts.encode() + b"." + body, hashlib.sha256)
    return hmac.compare_digest(sig, "v1=" + mac.hexdigest())

مرجع الواجهة

كل نقاط الوصول

يُقرأ مباشرةً من /v1/openapi.yaml، عقد OpenAPI 3.1 الذي يُختبر الخادم على أساسه. وجّه مولّد العملاء إلى العنوان نفسه.