٠. الربط بضغطة زر (بدون لصق مفتاح)
الطريقة المفضّلة: صاحب العيادة يضغط «اربط مع المعمل» داخل نظامه، فيُفتح له في المعمل صفحة موافقة، يسجّل دخوله بحسابه في المعمل ويوافق — ثم يعود إلى نظامه مرتبطاً. المفتاح لا يمر عبر متصفحه أبداً.
- يسجّل المعمل نظامك مرة واحدة ويعطيك:
app_idوapp_secretونطاق العودة المسموح (نطاق موقع نظامك). - لكل مستخدم: وجّهه إلى صفحة الموافقة بـ
app_idوredirect_uriوstateمن عندك. - بعد الموافقة يعود المتصفح إلى
redirect_uri?code=…&state=…— الرمز صالح ٥ دقائق ولمرة واحدة. - خادمك يستبدل الرمز بالمفتاح عبر
POST /connect/exchangeويحفظ المفتاح مشفَّراً لهذا المستخدم. - الرفض يعود بـ
redirect_uri?error=access_denied. - حساب غير معتمد في المعمل يرى «حسابك بانتظار اعتماد المعمل» ولا يُنشأ له ربط.
https://cadcam.algomaei.com/connect?app_id=<APP_ID>&redirect_uri=https%3A%2F%2Fclinic-hub.example.com%2Flab%2Freturn&state=<RANDOM>
curl -X POST https://cadcam.lovable.app/api/public/partner/connect/exchange \
-H "x-app-id: <APP_ID>" -H "x-app-secret: <APP_SECRET>" \
-H "Content-Type: application/json" \
-d '{ "code": "<CODE_FROM_REDIRECT>" }'
# { "api_key": "lab_live_…", "account": { "id": "…", "name": "عيادة النور", "type": "clinic" } }curl -X POST https://cadcam.lovable.app/api/public/partner/connect/revoke -H "Authorization: Bearer lab_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
state عند العودة، ولا ترسل app_secret ولا المفتاح إلى المتصفح. إن أردت الطريقة اليدوية القديمة (مفتاح يعطيه لك المعمل مباشرة) فهي تعمل كما هي وبقية التوثيق ينطبق عليها.١. نظرة عامة
واجهة الربط تسمح لأي نظام إدارة عيادات (مثل clinic-hub) بالعمل مع معمل الجُماعي كاد كام مباشرة: قراءة كتالوج الأعمال بأسعار حساب العيادة نفسها، إرسال الحالة من ملف المريض بضغطة واحدة، إرفاق ملفات السكان والتصميم، متابعة مراحل التنفيذ، وقراءة كشف الحساب.
- الحالة المُرسَلة تُنشأ داخل المعمل كأي حالة عادية: رقم رسمي، تسعير من كتالوج المعمل، مراحل إنتاج، وإشعار للاستقبال.
- كل مفتاح ربط مرتبط بحساب عيادة واحد، ولا يرى أو يكتب أي بيانات لحساب آخر.
- الأسعار والخصومات وحالات الإنتاج لا يمكن تغييرها من الخارج — التسعير من المعمل دائماً.
- كل نداء يُسجَّل في المعمل (الوقت، النداء، النتيجة) ويظهر للمدير.
٢. التوثيق والرابط الأساسي
الرابط الأساسي لكل النقاط:
https://cadcam.lovable.app/api/public/partner
كل طلب يحمل المفتاح في ترويسة Authorization (أو x-lab-key إن كان العميل لا يسمح بتعديل Authorization):
Authorization: Bearer lab_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Content-Type: application/json
- شكل المفتاح:
lab_live_+ ٤٨ حرفاً ستّ عشرياً. - المفتاح سرّي — يُستخدم من خادم نظام العيادة فقط، لا من المتصفح.
- بدون مفتاح صالح أو مع مفتاح معطّل: الرد
401مع{ "error": "unauthorized" }. - كل النقاط تدعم
OPTIONS(CORS) وترد بترويساتAccess-Control-Allow-*. - كل الردود بصيغة JSON بترميز UTF-8 وبلا تخزين مؤقت.
٣. البداية السريعة (٣ خطوات)
- اختبر المفتاح بـ
GET /ping— يعيد اسم الحساب ونوعه. - اجلب
GET /catalogواحفظwork_types[].idلعرضها في نموذج الإرسال داخل العيادة. - أرسل الحالة بـ
POST /casesمعexternal_refمن رقم الزيارة عندك، ثم تابعها بـGET /cases/{code}.
curl -H "Authorization: Bearer $LAB_API_KEY" https://cadcam.lovable.app/api/public/partner/ping
{
"ok": true,
"lab": "معمل الجُماعي كاد كام",
"account": { "id": "7f0c…", "name": "عيادة النور", "type": "clinic" }
}٤. كتالوج الأعمال والأسعار — GET /catalog
يعيد مجموعات الأعمال وأنواعها وحقولها الإضافية، مع سعر حساب هذه العيادة تحديداً: حساب نوعه lab يأخذ سعر المعمل، وغيره يأخذ سعر الطبيب.
curl -H "Authorization: Bearer $LAB_API_KEY" https://cadcam.lovable.app/api/public/partner/catalog
{
"groups": [{ "id": "…", "name": "الزيركون", "description": null, "sort_order": 1 }],
"work_types": [
{
"id": "8c1f…",
"group_id": "…",
"name": "تاج زيركون",
"case_type": "digital",
"unit": "قطعة",
"price": 9000,
"currency": "YER",
"fields": [
{ "id": "…", "label": "نوع الحافة", "field_type": "select", "required": true, "options": ["كتف", "شامفر"] }
]
}
]
}٥. إرسال حالة — POST /cases
حقول الجسم:
| الحقل | النوع | إلزامي | الوصف |
|---|---|---|---|
| external_ref | string ≤120 | مستحسن جداً | معرّفك عندك (رقم الزيارة/العمل). يمنع التكرار عند إعادة الإرسال. |
| patient_name | string ≤160 | نعم | اسم المريض. |
| patient_age | integer 0–130 | لا | العمر. |
| patient_gender | "male" | "female" | لا | الجنس. |
| case_type | "digital" | "traditional" | لا | رقمية (سكان) أو تقليدية (طبعة). الافتراضي: رقمية. |
| priority | "normal" | "urgent" | لا | الأولوية. |
| due_date | YYYY-MM-DD | لا | تاريخ التسليم المطلوب. |
| notes | string ≤2000 | لا | ملاحظات الطبيب. |
| items | array 1–30 | نعم | بنود العمل (انظر الجدول التالي). |
كل بند داخل items:
| الحقل | النوع | إلزامي | الوصف |
|---|---|---|---|
| work_type_id | uuid | نعم | من /catalog. |
| teeth | array من أرقام 11–85 | لا | الأسنان بنظام FDI (11 = الأيمن العلوي الأمامي). |
| bridges | array of arrays | لا | مجموعات الجسور، مثل [[11,12,13]]. |
| quantity | integer 1–200 | لا | عدد القطع. الافتراضي: عدد الأسنان أو 1. |
| shade | string ≤40 | لا | اللون، مثل A2. |
| notes | string ≤1000 | لا | ملاحظة البند. |
| field_values | object | لا | قيم الحقول الإضافية للعمل من fields في الكتالوج. |
curl -X POST https://cadcam.lovable.app/api/public/partner/cases \
-H "Authorization: Bearer $LAB_API_KEY" -H "Content-Type: application/json" \
-d '{
"external_ref": "clinic-visit-8841",
"patient_name": "أحمد محمد",
"patient_age": 34,
"patient_gender": "male",
"case_type": "digital",
"priority": "normal",
"due_date": "2026-10-05",
"notes": "اللون A2 — تسليم قبل الخميس",
"items": [
{ "work_type_id": "8c1f…", "teeth": [11, 12], "shade": "A2" },
{ "work_type_id": "8c1f…", "bridges": [[14, 15, 16]], "quantity": 3 }
]
}'{ "id": "b2d9…", "code": "25-1043", "duplicate": false }codeهو رقم الحالة الرسمي في المعمل — اعرضه للطبيب واستخدمه في المتابعة.- إعادة إرسال نفس
external_refتعيد نفس الحالة معduplicate: trueوالرمز200— آمن لإعادة المحاولة بعد انقطاع الشبكة. - المعمل يمنع أيضاً تكرار نفس المريض + نفس نوع الحالة خلال دقيقتين، ويرد بخطأ واضح.
- الأسعار تُحسب في المعمل من الكتالوج؛ أي سعر ترسله يُتجاهل.
٦. إرفاق ملف — POST /files
يُرفق ملف سكان أو تصميم أو صورة بحالة قائمة لنفس الحساب: STL / PLY / OBJ / HTML / صور / PDF. الحد الأقصى ٢٥ ميجابايت للملف الواحد، ويُرسل مُرمّزاً Base64.
| الحقل | النوع | إلزامي | الوصف |
|---|---|---|---|
| case | string | نعم | رقم الحالة (code) أو معرّفها (id). |
| name | string ≤200 | نعم | اسم الملف مع الامتداد، مثل upper.stl. |
| content_base64 | string | نعم | محتوى الملف Base64 (يُقبل أيضاً data:…;base64, كبادئة). |
| content_type | string ≤120 | لا | نوع المحتوى إن عرفته. |
curl -X POST https://cadcam.lovable.app/api/public/partner/files \
-H "Authorization: Bearer $LAB_API_KEY" -H "Content-Type: application/json" \
-d "{\"case\":\"25-1043\",\"name\":\"upper.stl\",\"content_base64\":\"$(base64 -w0 upper.stl)\"}"{
"file": { "id": "…", "url": "/api/public/media/cases/partner/…stl", "name": "upper.stl", "kind": "partner", "created_at": "…" },
"case": { "id": "b2d9…", "code": "25-1043" }
}٧. المتابعة — GET /cases و GET /cases/{code}
GET /cases يعيد حالات هذا الحساب فقط. معاملات الاستعلام: status (حالة الإنتاج)، search (رقم الحالة أو اسم المريض)، limit (١–٢٠٠، الافتراضي ٥٠).
curl -H "Authorization: Bearer $LAB_API_KEY" "https://cadcam.lovable.app/api/public/partner/cases?limit=20&search=أحمد" curl -H "Authorization: Bearer $LAB_API_KEY" https://cadcam.lovable.app/api/public/partner/cases/25-1043
GET /cases/{code_or_id} يعيد التفاصيل الكاملة: البنود، مراحل الإنتاج وحالتها، الملفات، طلبات موافقة التصميم، الدفعات، والرصيد المتبقي على الحالة.
{
"case": { "id": "b2d9…", "code": "25-1043", "external_ref": "clinic-visit-8841", "status": "in_progress",
"delivery_status": "pending", "due_date": "2026-10-05", "total_price": 18000, "discount": 0, "currency": "YER" },
"items": [{ "work_type_name": "تاج زيركون", "teeth": [11, 12], "quantity": 2, "unit_price": 9000 }],
"stages": [{ "name": "التصميم", "department": "cad", "status": "done", "completed_at": "…" },
{ "name": "القص", "department": "milling", "status": "in_progress" }],
"files": [{ "name": "upper.stl", "url": "/api/public/media/…" }],
"reviews": [{ "id": "…", "image_url": "…", "message": "يرجى الموافقة على التصميم", "status": "pending" }],
"payments": [{ "amount": 10000, "currency": "YER", "receipt_no": "R-221" }],
"balance": 8000
}reviews عنصر بحالة pending فهناك تصميم ينتظر موافقة الطبيب — اعرضه كتنبيه في نظام العيادة.٨. كشف الحساب — GET /statement
إجماليات مفصولة بالعملة (رصيد افتتاحي + مستحق − مدفوع = الرصيد)، مع قائمة الحالات والدفعات. معاملات اختيارية: from وto بصيغة تاريخ ISO.
curl -H "Authorization: Bearer $LAB_API_KEY" "https://cadcam.lovable.app/api/public/partner/statement?from=2026-09-01"
{
"totals": { "YER": { "opening": 0, "billed": 480000, "paid": 300000, "balance": 180000 } },
"cases": [{ "code": "25-1043", "patient_name": "أحمد محمد", "total_price": 18000, "currency": "YER" }],
"payments": [{ "amount": 10000, "currency": "YER", "receipt_no": "R-221", "created_at": "…" }]
}٩. الإشعار العكسي (Webhook)
يضيف مدير المعمل عنوان نظام العيادة (https فقط) لكل مفتاح، فيصل إليه إشعار فوري عند كل تغيّر مهم — بدون انتظار أو استعلام متكرر.
| الحدث | متى يُرسل | أهم الحقول |
|---|---|---|
| case.updated | تغيّر حالة الإنتاج أو حالة التسليم | id, code, external_ref, status, delivery_status, patient_name, due_date, delivered_at |
| payment.recorded | تسجيل دفعة على حالة | order_id, code, amount, currency, method, receipt_no, created_at |
| review.requested | طلب موافقة على التصميم | order_id, code, review_id, image_url, message, status |
{
"event": "case.updated",
"sent_at": "2026-09-19T08:14:22.104Z",
"data": { "id": "b2d9…", "code": "25-1043", "external_ref": "clinic-visit-8841",
"status": "ready", "delivery_status": "pending", "patient_name": "أحمد محمد" }
}- الترويسات:
X-Lab-Eventباسم الحدث، وX-Lab-Signature= HMAC SHA-256 (hex) لنص الجسم الخام بسر التوقيع. - سر التوقيع يظهر لمدير المعمل في صفحة «الربط مع الأنظمة» بجانب العنوان.
- تحقّق من التوقيع دائماً على النص الخام قبل تحليل JSON، بمقارنة ثابتة الزمن.
- ردّ بسرعة بـ 200؛ المعالجة الطويلة تكون بعد الرد. عالج الإشعار بشكل مُتحمّل للتكرار (اعتمد
code+status).
import { createHmac, timingSafeEqual } from "crypto";
export async function handleLabWebhook(request: Request) {
const raw = await request.text(); // النص الخام أولاً
const sent = request.headers.get("x-lab-signature") ?? "";
const expected = createHmac("sha256", process.env.LAB_WEBHOOK_SECRET!)
.update(raw)
.digest("hex");
const a = Buffer.from(sent, "utf8");
const b = Buffer.from(expected, "utf8");
if (a.length !== b.length || !timingSafeEqual(a, b)) {
return new Response("invalid signature", { status: 401 });
}
const { event, data } = JSON.parse(raw);
// حدّث العمل عندك بحسب data.external_ref أو data.code
return new Response("ok");
}١٠. الأخطاء ومعالجتها
| الرمز | error | المعنى وما تفعله |
|---|---|---|
| 401 | unauthorized | المفتاح ناقص أو خاطئ أو معطّل — راجع الترويسة، أو اطلب مفتاحاً جديداً من المعمل. |
| 400 | invalid_json | الجسم ليس JSON صالحاً. |
| 400 | invalid_body | حقول ناقصة أو غير صحيحة — التفاصيل في issues[] (مسار الحقل والسبب). |
| 400 | create_failed | رفض من المعمل (مثل نوع عمل غير موجود أو حالة مكررة) — الرسالة في message بالعربية. |
| 404 | not_found | لا توجد حالة بهذا الرقم لهذا الحساب. |
| 413 | file_too_large | الملف يتجاوز ٢٥ ميجابايت — قسّمه أو اضغطه. |
| 500 | read_failed / upload_failed / attach_failed | خطأ مؤقت — أعد المحاولة بتأخير متزايد. |
- أعد المحاولة عند 429/500/503 بتأخير متزايد (1s، 4s، 15s).
- لا تُعد إنشاء الحالة بعد انقطاع دون
external_ref— معه الإعادة آمنة تماماً. - سجّل رمز الحالة و
messageعندك لتشخيص أسرع؛ المدير يرى نفس النداءات في المعمل.
١١. للمساعد الذكي (AI agent)
إن كنت تبني الربط داخل نظام العيادات بمساعدة وكيل ذكي، أعطه هذه الروابط مباشرة:
التوثيق كنص خام (Markdown): https://cadcam.algomaei.com/docs/integration.md وصف الواجهة (OpenAPI 3.1): https://cadcam.algomaei.com/api/public/partner/openapi.json
وهذه المهمة الجاهزة للصق في الوكيل:
اقرأ https://cadcam.algomaei.com/docs/integration.md ثم أضف في هذا المشروع:
1) شاشة «إعدادات الربط بالمعمل»: حفظ مفتاح lab_live_ في أسرار الخادم + زر اختبار الاتصال عبر GET /ping.
2) في ملف المريض زر «إرسال للمعمل»: يجلب /catalog ويعرض أنواع الأعمال وأسعارها، يختار الأسنان بنظام FDI،
ثم POST /cases مع external_ref = معرّف الزيارة، ثم POST /files لكل ملف سكان.
3) شاشة «أعمالي في المعمل»: GET /cases و GET /cases/{code} مع تنبيه عند وجود طلب موافقة تصميم، و GET /statement للرصيد.
4) نقطة استقبال إشعارات: تتحقق من X-Lab-Signature (HMAC SHA-256 على النص الخام) قبل التحديث.
كل النداءات من الخادم فقط، والمفتاح لا يظهر في المتصفح.١٢. قواعد وحدود
- المفتاح يقرأ ويكتب بيانات حسابه فقط — لا وصول لأي حالة أو حساب آخر.
- لا يمكن تعديل الأسعار ولا الخصومات ولا مراحل الإنتاج من الخارج.
- تعطيل المفتاح من المعمل يوقف الربط فوراً دون أي أثر على الحالات السابقة.
- الأسنان بنظام FDI؛ الجسور مجموعات أرقام متصلة.
- العملات كما هي في المعمل (YER / USD) ولا تُحوَّل تلقائياً.
- حد الملف ٢٥ ميجابايت؛ حد بنود الحالة ٣٠ بنداً.
- الملفات تُخزَّن في المعمل وتظهر للفنيين مع الحالة.