من رابطٍ تُرسله في واتساب إلى واجهةٍ برمجيّةٍ كاملة. ابدأ بالأسهل، وانتقل متى احتجت — فالغرفةُ نفسُها والأدواتُ نفسُها في كلّ الطرق، وكلُّ ميزةٍ مفتاحٌ تضبطه مؤسّستك.
لكلّ حلقةٍ رابطان: للمعلّم (يُدير الحصّة ويُسجّلها) وللطلّاب. يفتح الطالبُ الرابط ويكتب اسمه ويدخل — بلا حسابٍ ولا تطبيق. والرابطُ نفسه لكلّ حصص الحلقة حتّى تنقضي مدّته، وملاحظاتُ المعلّم ودفترُ التسميع تتبع الحلقة.
curl -X POST https://api.halqa.live/v1/invites \
-H "Authorization: Bearer $HALQA_SECRET" -H "Content-Type: application/json" \
-d '{"label":"halaqa-fajr","days":30}'
# ← { "teacher": "https://halqa.live/j/…", "student": "https://halqa.live/j/…", "embed": "<iframe …>" }
خذ رابطَ الطلّاب من الطريقة ١ وضعه في إطار. هذا كلُّ شيء — الحصّةُ داخل صفحتك بهويّة مؤسّستك.
<iframe src="رابط الطلّاب"
allow="camera; microphone; display-capture; fullscreen; autoplay; screen-wake-lock"
allowfullscreen style="width:100%;height:80vh;border:0;border-radius:12px"></iframe>
allow ضروريّ: بدونه يمنع المتصفّحُ الميكروفونَ والكاميرا داخل الإطار.
وبعضُ منشئي المواقع يضع الكود في إطارٍ ثانٍ يحذف هذا السطر — إن لم يعمل الميكروفون فضع رابطًا يفتح الحصّةَ في صفحتها بدل الإطار.[halqa room="halaqa-fajr"]
// والزوّارُ بلا حساب يدخلون أيضًا (يكتبون أسماءهم):
[halqa room="halaqa-fajr" guests="yes"]
نزّل إضافة ووردبريس ↓
من يملك صلاحيّة «تحرير مقالات الآخرين» أو halqa_teach. ولتحديده بنفسك (مع LearnDash أو Tutor LMS مثلًا):
add_filter('halqa_role', function ($role, $user) {
return in_array('tutor_instructor', $user->roles) ? 'host' : $role;
}, 10, 2);
نداءان من خادمك — والسرُّ لا يغادره أبدًا: (١) غرفةٌ للحصّة بمعرّفها عندك، (٢) رمزُ دخولٍ لهذا المستخدم بدوره. ثمّ اعرض الغرفةَ في إطارٍ أو صفحة. ونفسُ المعرّف يُعيد الغرفةَ نفسَها، فلا تُخزّن شيئًا.
const H = { Authorization: `Bearer ${process.env.HALQA_SECRET}`, "Content-Type": "application/json" }; const post = (p, b) => fetch("https://api.halqa.live" + p, { method: "POST", headers: H, body: JSON.stringify(b) }).then((r) => r.json()); export async function halqaUrl({ lessonId, classId, user, role }) { const room = await post("/v1/rooms", { externalId: `lesson-${lessonId}`, classKey: `class-${classId}` }); const { token } = await post("/v1/tokens", { roomId: room.roomId, identity: user.id, name: user.name, role }); return `https://api.halqa.live/room#t=${token}`; // role: host · cohost · participant · supervisor · observer }
function halqa_post($path, $body) { $ch = curl_init("https://api.halqa.live" . $path); curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv("HALQA_SECRET"), "Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode($body)]); return json_decode(curl_exec($ch), true); } $room = halqa_post("/v1/rooms", ["externalId" => "lesson-$lessonId", "classKey" => "class-$classId"]); $token = halqa_post("/v1/tokens", ["roomId" => $room["roomId"], "identity" => $userId, "name" => $userName, "role" => $isTeacher ? "host" : "participant"])["token"]; $url = "https://api.halqa.live/room#t=" . $token;
import os, requests H = {"Authorization": f"Bearer {os.environ['HALQA_SECRET']}"} def post(p, b): return requests.post("https://api.halqa.live" + p, json=b, headers=H, timeout=10).json() def halqa_url(lesson_id, class_id, user, role): room = post("/v1/rooms", {"externalId": f"lesson-{lesson_id}", "classKey": f"class-{class_id}"}) tok = post("/v1/tokens", {"roomId": room["roomId"], "identity": str(user.id), "name": user.name, "role": role}) return "https://api.halqa.live/room#t=" + tok["token"]
curl -X POST https://api.halqa.live/v1/rooms -H "Authorization: Bearer $HALQA_SECRET" \
-H "Content-Type: application/json" -d '{"externalId":"lesson-42","classKey":"class-7"}'
# ← { "roomId": "cm…" }
curl -X POST https://api.halqa.live/v1/tokens -H "Authorization: Bearer $HALQA_SECRET" \
-H "Content-Type: application/json" -d '{"roomId":"cm…","identity":"u-9","name":"سارة","role":"participant"}'
# ← { "token": "…" } ثمّ: https://api.halqa.live/room#t=<token>
أضف &exit= برابط صفحتك للعودة بعد الخروج، واستمع لحدث الخروج:
addEventListener("message", (e) => { if (e.origin !== "https://api.halqa.live") return; if (e.data?.type === "halqa:left") location.href = "/my-lessons"; if (e.data?.type === "halqa:time") draw(e.data.endsAt, e.data.serverNow); // عدّادك وزرّ التمديد if (e.data?.type === "halqa:ended") done(e.data.reason); // scheduled | host | admin });
إن كنتَ تبيع الحصّةَ كتلةَ دقائق، أرسِل موعدَها عند الإنشاء. والموعدُ اختياريّ — غرفةٌ بلا موعدٍ تعمل كما كانت: يفتحها المعلّم ويغلقها.
POST /v1/rooms { "externalId": "lesson-42", "startsAt": "2026-11-02T17:00:00Z", "endsAt": "2026-11-02T17:45:00Z" }
// ← { roomId, startsAt, endsAt, closesAt, serverNow }
| ما يحدث | متى |
|---|---|
| يُفتح الباب ويُصدَر الرمز | قبل startsAt بعشر دقائق — وقبلها يُرَدّ النداءُ بـ425 |
| سطرٌ هادئ «بقيت خمس دقائق» داخل الغرفة | قبل النهاية بخمس |
| تنتهي الحصّةُ للجميع وتُغلق الغرفة | عند closesAt = endsAt + دقائق السماح |
| الرمزُ يموت | عند closesAt نفسِه — فرابطٌ تسرَّب يموت مع غرفته |
النداءُ ثانيةً بنفس externalId يُعيد الغرفةَ نفسَها، وبنهايةٍ أبعد يُحرّك النهاية.
أو صراحةً: POST /v1/rooms/{roomId}/extend {"minutes":15} — يُعيد endsAt الجديد.
والقرارُ كلُّه عندك: نحن لا نُسعّر ولا نُحصّل ولا نَعُدّ بالثانية.
اضبط لنا webhookUrl وwebhookSecret لمؤسّستك، فتصلك ثلاثةُ أحداث:
room.started وparticipant.joined وparticipant.left وroom.ended
(ومعه reason: scheduled · host · admin) وreport.ready
وrecording.ready. ومع كلٍّ منها externalId وclassKey، ومع أحداث المشارك metadata.
تُستعمَل لغياب المعلّم وردّ المال وسجلِّ الحضور عند النزاع — لا للمحاسبة بالثانية (تلك في /v1/usage).
// التحقّقُ من التوقيع — رُدَّ ما مضى عليه خمس دقائق const body = await req.text(); const ts = req.headers.get("X-Halqa-Timestamp"); const want = "sha256=" + crypto.createHmac("sha256", SECRET).update(ts + "." + body).digest("hex"); if (want !== req.headers.get("X-Halqa-Signature")) return new Response("bad sig", { status: 401 });
ثلاثُ محاولاتٍ عند الفشل، ومهلةُ خمسِ ثوانٍ لكلٍّ منها — وخادمٌ نائمٌ عندك لا يُوقف صفًّا يدرس.
أرسِل مع الرمز metadata (أيُّ كائنٍ حتّى ٢٠٠٠ حرف)، فتعود كما هي في
GET /v1/reports وفي كلّ خطّافٍ عن صاحبها. بها تربط الحضورَ بسجلّاتك بلا مطابقةٍ بالأسماء
(و«محمد» ثلاثةٌ في الصفّ الواحد). ولا نُفسّرها ولا نبحث فيها — كـclassKey سواءً بسواء.
POST /v1/tokens { "roomId": "…", "identity": "u-9", "role": "participant",
"metadata": { "studentId": "s-9931", "book": "qiraa-3", "unit": 7 } }
metadata تسكن في الرمز الموقَّع ويقرؤها متصفّحُ صاحبه — فضَع معرّفاتٍ لا أسرارًا.عمرُ الرمز ساعة. فإن طالت حصّتُك، تطلب الغرفةُ منك رمزًا جديدًا قبل انتهائه بخمس دقائق، وترُدّ أنت برمزٍ جديد — فلا ينقطع أحد:
if (e.data?.type === "halqa:token-expiring") { const { token } = await post("/v1/tokens", { roomId: e.data.roomId, identity: e.data.identity, role }); frame.contentWindow.postMessage({ type: "halqa:token", token }, "https://api.halqa.live"); }
المقبسُ القائم يصمد بعد انتهاء الرمز، لكنّ أوّلَ انقطاعٍ بعده لا عودةَ منه — وهنا يقع الضرر.
نضع frame-ancestors على صفحة الغرفة، فلا يُضمّنها إلّا نطاقاتُك التي سجّلناها لك.
أعطِنا نطاقاتِك (https://x.com https://*.x.com) قبل الإطلاق، وإلّا ظهرت الغرفةُ فارغةً داخل صفحتك.
ومررّ ?t=<publicKey> في عنوان الإطار لتُقصَر القائمةُ على نطاقاتك وحدَها.
http://localhost:3000) نسجّله لك، لكنّه
يعمل بـ?t=<publicKey> وحدَه: صفحةُ الغرفة ساكنةٌ تُخدَم بقائمةٍ واحدةٍ لكلّ
عملائنا، ولو دخلها جهازُ مبرمجٍ عندك لصار يُضمّن غرفَ غيرك. فضَع ?t= في إطارك دائمًا.| النداء | ما يُعيده |
|---|---|
GET /v1/recordings?externalId=lesson-42 | تسجيلاتُ الحصّة (إن أتحتَ التسجيل) |
POST /v1/recordings/:id/link | رابطُ مشاهدةٍ قصيرُ العمر لطالبٍ أو وليّ أمر |
GET /v1/reports?externalId=lesson-42 | تقريرُ الحصّة: الحضور، ومن سمّع وأخطاؤه، والواجب، والاختبارات |
GET /v1/students/u-9?classKey=class-7 | دفترُ تسميع الطالب في صفّه: آخرُ تسميع، وخطؤه المعتاد، وواجبه — لملفّ الطالب عندك |
POST /v1/console-link | لوحةُ منصّتك للقراءة؛ و{"manage":true} لرابط إدارةٍ يُنشئ روابط الدعوة أيضًا |
GET /v1/usage | الاستهلاكُ اليوميّ للفوترة |
classKey معرّفُ «الصفّ» عندك (حلقةٌ، أو معلّمٌ وطالب) — به تتبع ملاحظاتُ المعلّم
ودفترُ التسميع الصفَّ من حصّةٍ إلى حصّة. اختياريّ، وأنت تقرّر معناه.أخبرنا بما تستعمل وكيف يدخل طلّابك اليوم، ونقترح عليك أقصرَ طريق — غالبًا يكفي واحدٌ من الأربعة أعلاه بتعديلٍ صغير، وإلّا بنيناه معك. ولا نَعِدُ بما لم نبنِه: نقول لك ما يعمل اليوم وما يحتاج وقتًا.
اطلب دمجًا خاصًّا| إن كنت… | ابدأ بـ | الطلّاب يدخلون بـ |
|---|---|---|
| معلّمًا أو مركزًا صغيرًا بلا موقع | ١ · روابط | اسمٍ يكتبونه |
| لك موقعٌ على Wix أو Google Sites | ٢ · تضمين | اسمٍ يكتبونه |
| لك موقعُ ووردبريس بعضويّات | ٣ · الإضافة | حساباتهم في موقعك |
| منصّةً بحساباتٍ وأدوار (أولياءُ أمورٍ ومشرفون) | ٤ · الواجهة البرمجيّة | حساباتهم وأدوارهم |
| مدرسةً أو جامعةً على Moodle أو Canvas | ٢ · كتلةُ HTML في المقرّر | اسمٍ يكتبونه |
| لك تطبيقٌ أو نظامٌ لا تجده هنا | ٥ · طلبٌ خاصّ | ما نتّفق عليه |
وفي كلّ الطرق: هويّةُ مؤسّستك (الشعارُ والألوان)، وكلُّ أداةٍ مفتاحٌ تُشعله أو تطفئه. تواصل معنا للحصول على سرّ مؤسّستك.