١. مقدمة وبداية سريعة
تتيح هذه البوابة لنظامك سحب بيانات الحضور/البصمات الخاصة بمؤسستك وإدارة أجهزتها، عبر توكن واحد خاص بمؤسستك. كل طلب يُحدِّد مؤسستك تلقائياً — لن ترى إلا بياناتك.
كل المسارات تبدأ بـ /api/v1. أول تجربة — تحقّق من توكنك:
curl -H "Authorization: Bearer __TOKEN__" \
__BASE__/api/v1/meإن رجع "ok": true ومعلومات مؤسستك فالتوكن يعمل.
٢. المصادقة
أرسل التوكن في ترويسة Authorization مع كل طلب:
Authorization: Bearer __TOKEN__٣. سحب البيانات (قراءة)
__BASE__/api/v1/meمعلومات مؤسستك + أعداد الأجهزة والطلاب.
__BASE__/api/v1/devicesأجهزتك (بصمات مؤسستك) وحالة اتصالها الآن (online حيّ، وليس قيمة قديمة). يُرجع لكل جهاز sn وname وonline — استخدم sn لاختيار الأجهزة عند إضافة مستخدم في «إدارة الأجهزة».
__BASE__/api/v1/studentsالطلاب/الموظفون المسجّلون على أجهزتك (الرقم والاسم).
__BASE__/api/v1/attendanceسجلات الحضور/البصمات (الأحدث أولاً) مع ترقيم بالمؤشّر (cursor).
| المعامل | الوصف |
|---|---|
limit | عدد السجلات (١–٥٠٠، الافتراضي ١٠٠) |
since | من تاريخ (ISO مثل 2026-06-23T00:00:00Z) |
until | إلى تاريخ (ISO) |
cursor | مؤشّر الصفحة التالية (من nextCursor) |
مثال الرد:
{
"ok": true,
"attendance": [
{
"sn": "STC010083287",
"enrollid": 1,
"name": "علي رياض",
"time": "2026-06-19 14:51:06",
"eventAt": "2026-06-19T11:51:06.000Z",
"inout": 0,
"mode": 8,
"imageUrl": "/images/..._.jpg",
"createdAt": "2026-06-19T11:51:07.000Z"
}
],
"hasMore": true,
"nextCursor": "eyJ0Ijox..."
}inout: 0 = دخول، 1 = خروج · mode: 8/15 = وجه، 0/1 = بصمة إصبع.
٤. إدارة الأجهزة
أوامر تُرسَل للجهاز وتُرجع فوراً 202 مع commandId (لا تنتظر رد الجهاز). أوامر جهاز واحد (الأسفل) تتطلّب أن يكون الجهاز متّصلاً (وإلا 409)، أمّا إضافة مستخدم للمؤسسة فتُحفظ في طابور دائم وتصل للأجهزة غير المتصلة عند عودتها.
__BASE__/api/v1/studentsإضافة/تسجيل مستخدم إلى مؤسستك وأجهزتها المختارة دفعةً واحدة. نوع الجسم: multipart/form-data (لرفع صورة الوجه كملف). الحقول:
| الحقل | الوصف |
|---|---|
enrollid | رقم المستخدم — مطلوب |
name | الاسم |
admin | 0 مستخدم · 1 مدير (يدخل قائمة إعدادات الجهاز) |
password | كلمة مرور رقمية بلا أصفار بادئة (اختياري) |
card | رقم بطاقة رقمي بلا أصفار بادئة (اختياري) |
image | ملف صورة الوجه JPEG/PNG (اختياري) — تُسجَّل على كل جهاز مختار، كإضافة الصورة في الواجهة |
devices | الأرقام التسلسلية للأجهزة (من GET /api/v1/devices) — كرّر الحقل لكل جهاز. احذفه = كل أجهزة المؤسسة. غير المملوكة تُهمَل وتظهر في skipped |
لا يشترط اتصال الأجهزة — تصلها الإضافة عند عودتها.
مثال الاستعمال (form-data عبر curl):
curl -X POST "__BASE__/api/v1/students" \
-H "Authorization: Bearer __TOKEN__" \
-F "enrollid=20" \
-F "name=طالب جديد" \
-F "admin=0" \
-F "password=1234" \
-F "card=123456" \
-F "image=@face.jpg" \
-F "devices=STC010083286" \
-F "devices=STC010099001"مثال الرد:
{
"ok": true,
"enrolled": 2,
"devices": [
{ "sn": "STC010083286", "online": true },
{ "sn": "STC010099001", "online": false }
],
"skipped": []
}بديل برمجي: يمكنك أيضاً الإرسال بصيغة application/json — مع devices كمصفوفة وimage كنص data:URL (base64).
__BASE__/api/v1/students/:enrollidتعديل مستخدم في مؤسستك: الاسم / الصلاحية (admin) / كلمة المرور / البطاقة / صورة الوجه، وكذلك أجهزته. نفس حقول الإضافة (multipart/form-data لرفع صورة، أو application/json). مرّر devices لضبط مجموعة الأجهزة (يُضاف للجديدة ويُحذف من المحذوفة)، أو احذفه لإبقائها كما هي. الحقول غير المُرسَلة تبقى دون تغيير. لحذف البطاقة أرسل الحقل removeCard=true.
مثال (تغيير الصورة والصلاحية):
curl -X PATCH "__BASE__/api/v1/students/20" \
-H "Authorization: Bearer __TOKEN__" \
-F "admin=1" \
-F "image=@new-face.jpg" \
-F "devices=STC010083286"__BASE__/api/v1/students/:enrollidحذف المستخدم نهائياً من المؤسسة وإزالته من كل أجهزتها. (لحذفه من جهاز واحد فقط استخدم DELETE /api/v1/devices/:sn/users/:enrollid بالأسفل.)
__BASE__/api/v1/devices/:sn/usersتسجيل/تعديل مستخدم على جهاز واحد محدّد (يتطلّب اتصال الجهاز). backupnum: 0–9 بصمة · 10 كلمة مرور · 11 بطاقة · 20–27 وجه · 50 صورة وجه.
الجسم (JSON): {"enrollid":20,"name":"طالب جديد","backupnum":0}
__BASE__/api/v1/devices/:sn/users/:enrollidحذف مستخدم. أضف ?backupnum= لحذف قالب محدّد (الافتراضي: الكل).
__BASE__/api/v1/devices/:sn/open-doorفتح الباب (لأجهزة التحكم بالدخول).
الجسم (اختياري): {"doornum":1}
__BASE__/api/v1/devices/:sn/syncسحب فوري من الجهاز. what: newlog (سجلات جديدة) أو userlist (المستخدمون) أو both.
الجسم (JSON): {"what":"both"}
٥. البث اللحظي (Real-time stream)
بدل السحب المتكرر، افتح اتصال WebSocket واحد فيصلك كل حضور لحظة حدوثه — معزولاً لمؤسستك فقط (توكنك يحدّد مؤسستك، ولا ترى أحداث غيرها).
__WSBASE__/api/v1/streamالمصادقة بترويسة Authorization: Bearer (للخوادم) أو ?token= (للمتصفح، لأنه لا يدعم ترويسات في WebSocket).
خطوات الربط
- افتح اتصال WebSocket إلى
__WSBASE__/api/v1/stream - المصادقة: من خادمك أرسل ترويسة
Authorization: Bearer ‹توكنك›؛ ومن المتصفح أضِف?token=‹توكنك›إلى الرابط (المتصفح لا يدعم الترويسات في WebSocket). - عند النجاح تصلك أولاً رسالة
{"type":"connected"}بمعلومات مؤسستك، ثم تتدفّق أحداث الحضور لحظة حدوثها. - عالِج الانقطاع: أعِد الاتصال تلقائياً عند
close(كما في مثال Node أدناه)، والخادم يرسل نبضةpingكل ٣٠ ثانية للإبقاء على الاتصال. - توكن خاطئ أو معطّل ⟵ يُرفض الاتصال بالرمز
401.
أنواع الأحداث الواردة:
{ "type": "connected", "institution": { "id": "...", "name": "...", "slug": "..." } }
{ "type": "attendance", "sn": "STC010083287", "enrollid": 1, "name": "علي رياض",
"time": "2026-06-19 14:51:06", "inout": 0, "mode": 8, "at": "2026-06-19T11:51:07.000Z" }
{ "type": "device", "sn": "STC010083287", "online": true, "at": "..." }Node.js (خادم — بترويسة Authorization)
const WebSocket = require("ws");
function connect() {
const ws = new WebSocket("__WSBASE__/api/v1/stream", {
headers: { Authorization: "Bearer __TOKEN__" }
});
ws.on("message", (raw) => {
const ev = JSON.parse(raw);
if (ev.type === "attendance")
console.log("حضور:", ev.enrollid, ev.name, ev.time, ev.inout ? "خروج" : "دخول");
});
ws.on("close", () => setTimeout(connect, 3000)); // أعد الاتصال تلقائياً
ws.on("error", () => {});
}
connect();المتصفح (التوكن في الرابط)
const ws = new WebSocket("__WSBASE__/api/v1/stream?token=__TOKEN__");
ws.onmessage = (e) => {
const ev = JSON.parse(e.data);
if (ev.type === "attendance") console.log("حضور:", ev.name, ev.time);
};401.٦. سحب الحضور تدريجياً (المؤشّر)
لجلب الجديد فقط في كل مرة: استخدم nextCursor من الرد السابق كـ cursor للطلب التالي، ودُر طالما hasMore = true. احفظ آخر مؤشّر لتبدأ منه لاحقاً.
since أقدم بقليل، أو طابِق السجلات بمفتاح enrollid + time.
٧. أمثلة تكامل بلغات مختلفة
سحب كل الحضور صفحةً صفحة:
const BASE = "__BASE__";
const TOKEN = "__TOKEN__";
async function pullAll(cursor) {
let url = `${BASE}/api/v1/attendance?limit=200`;
if (cursor) url += `&cursor=${encodeURIComponent(cursor)}`;
const res = await fetch(url, { headers: { Authorization: `Bearer ${TOKEN}` } });
const data = await res.json();
for (const rec of data.attendance) {
// خزّن rec في نظامك — المفتاح الفريد: enrollid + time
console.log(rec.enrollid, rec.name, rec.time);
}
if (data.hasMore) return pullAll(data.nextCursor);
return data.nextCursor; // احفظه لتبدأ منه لاحقاً
}
pullAll();<?php
$BASE = "__BASE__";
$TOKEN = "__TOKEN__";
function pull($BASE, $TOKEN, $cursor = null) {
$url = "$BASE/api/v1/attendance?limit=200";
if ($cursor) $url .= "&cursor=" . urlencode($cursor);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $TOKEN"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($data["attendance"] as $rec) {
// خزّن $rec في نظامك (enrollid + time مفتاح فريد)
}
if (!empty($data["hasMore"])) return pull($BASE, $TOKEN, $data["nextCursor"]);
return $data["nextCursor"];
}
pull($BASE, $TOKEN);import requests
BASE = "__BASE__"
TOKEN = "__TOKEN__"
H = {"Authorization": f"Bearer {TOKEN}"}
def pull(cursor=None):
params = {"limit": 200}
if cursor: params["cursor"] = cursor
data = requests.get(f"{BASE}/api/v1/attendance", headers=H, params=params).json()
for rec in data["attendance"]:
print(rec["enrollid"], rec["name"], rec["time"]) # خزّنه في نظامك
if data.get("hasMore"):
return pull(data["nextCursor"])
return data.get("nextCursor")
pull()٨. الأخطاء والحدود
كل الأخطاء بالشكل: {"ok": false, "error": "...", "message": "..."}
| الكود | المعنى |
|---|---|
| 400 | معامل ناقص أو غير صالح |
| 401 | توكن ناقص أو خاطئ |
| 404 | الجهاز غير موجود أو ليس ملكاً لمؤسستك |
| 409 | device_offline — الجهاز غير متصل الآن |
| 429 | تجاوزت حد الطلبات |
| 202 | أمر الإدارة قُبل وأُرسل للجهاز |
الحدود: القراءة ١٢٠ طلب/دقيقة · الإدارة ٣٠ طلب/دقيقة لكل توكن.
٩. ملاحظات مهمة
- العزل مضمون: توكنك يرى بيانات مؤسستك فقط.
- عدّة أجهزة: كل أجهزة مؤسستك تعمل بنفس التوكن.
- الصور:
imageUrlمرجعي فقط، لا يُجلب عبر هذه البوابة في الإصدار الحالي. - المفتاح الفريد للسجل:
sn + enrollid + time(قد تتكرر بصمتان في نفس الثانية بمؤشّر مختلف). - استخدم HTTPS دائماً في الإنتاج.