تخطَّ إلى المحتوى

واجهة REST API (Tool API وخادم إلى خادم)

حزمة المتصفح تتخاطب مع Tool API. يمكنك استدعاء الواجهة نفسها من خادمك الخلفي — لتسجيل الموافقات التي تُجمع في واجهتك الخاصة، أو لإصدار رموز مميزة (tokens) للموافقة لمرة واحدة، أو للبحث عن السجلات والتحقق منها.

عنوان URL الأساسي: https://system.hashentry.com

الطريقةالمسارالغرضالمصادقة
GET/tool/v1/configتكوين اللافتة، المستندات المرفقة، المنطقة المكتشَفةمفتاح API (قراءة)
GET/tool/v1/documents/{slug}/activeالإصدار النشط من مستند واحد، مع المحتوىمفتاح API (قراءة)
GET/tool/v1/documents/activeالمستندات النشطة المرفقة بالموقعمفتاح API (قراءة)
POST/tool/v1/consentsتسجيل موافقة واحدة (مسار حزمة المتصفح / الجوال)مفتاح API (كتابة) + توقيع
POST/tool/v1/consents/batchتسجيل ما يصل إلى 100 موافقةمفتاح API (كتابة) + توقيع
POST/tool/v1/s2s/consentsتسجيل موافقة واحدة من خادمك الخلفيمفتاح API (كتابة) + JWT + توقيع
POST/tool/v1/s2s/consents/batchتسجيل ما يصل إلى 100 موافقة من خادمك الخلفيمفتاح API (كتابة) + JWT + توقيع
POST/tool/v1/s2s/approval-tokensإصدار رمز مميز لمرة واحدة للموافقة على مستندمفتاح API (كتابة) + JWT + توقيع
GET/tool/v1/consents/statusالموافقات الحالية لزائر واحدمفتاح API (قراءة)
GET/tool/v1/consents/verify/{id}فحص سلامة سجل واحدمفتاح API (قراءة)
GET POST/api/v1/public/consents/verifyفحص سلامة عام (بلا مفتاح)لا شيء

كل طلب إلى Tool API يحمل مفتاح الموقع:

X-API-Key: he_live_…

للمفتاح نطاق صلاحية: read يسمح بنقاط النهاية GET، و write يسمح إضافةً إلى ذلك بـ POST. يجب أن يكون الموقع موثَّقًا في لوحة التحكم. إذا كانت للموقع قائمة أصول (origins) مسموح بها، تُرفض طلبات المتصفح الآتية من أصول أخرى؛ وإذا كانت له قائمة عناوين IP مسموح بها، تُرفض الطلبات الآتية من عناوين أخرى.

توقيع الطلب (جميع طلبات POST)

Section titled “توقيع الطلب (جميع طلبات POST)”
X-Signature: <HMAC-SHA256 بصيغة hex>
X-Timestamp: <وقت Unix بالثواني>
X-Nonce: <قيمة فريدة لكل طلب>
body_hash = SHA-256(جسم الطلب الخام) (hex بأحرف صغيرة)
canonical = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + body_hash
signature = HMAC-SHA256(key = مفتاح API, message = canonical) (hex بأحرف صغيرة)
  • METHOD بأحرف كبيرة. PATH هو مسار الطلب متضمنًا /tool/v1، دون سلسلة الاستعلام، مثل /tool/v1/s2s/consents.
  • تُحسب تجزئة الجسم على البايتات نفسها التي ترسلها بالضبط. حوّل الـ JSON إلى سلسلة مرة واحدة وأرسل تلك السلسلة نفسها.
  • يجب أن يكون X-Timestamp ضمن ±300 ثانية من وقت الخادم.
  • X-Nonce يتكون من 8–128 محرفًا من A–Z a–z 0–9 . _ : - (يصلح UUID) ولا يمكن استخدامه إلا مرة واحدة لكل موقع؛ ويُرفض التكرار بـ NONCE_REPLAYED.

مثال محلول (متجه الاختبار المشترك بين الحزمة والـ API):

API key : he_live_0123456789abcdef0123456789abcdef
Method : POST
Path : /tool/v1/consents
Timestamp : 1760000000
Nonce : 3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5b
Body : {"consent_type":"cookie","decision":"accepted","language":"tr","idempotency_key":"9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d","metadata":{"note":"çerez ✓"}}
body_hash : 93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34
canonical : POST\n/tool/v1/consents\n1760000000\n3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5b\n93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34
signature : e108683927badf35c36d5f467f63a9704c131d0102f14b06af7ed3046d44a8be

(\n يمثل محرف تغذية سطر واحدًا؛ والجسم بترميز UTF-8.) إذا أنتج تنفيذك هذا التوقيع لهذه المدخلات فهو صحيح.

Node.js
const crypto = require('crypto');
function sign(apiKey, method, path, body) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID();
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex');
const canonical = [method.toUpperCase(), path, timestamp, nonce, bodyHash].join('\n');
const signature = crypto.createHmac('sha256', apiKey).update(canonical, 'utf8').digest('hex');
return { 'X-Signature': signature, 'X-Timestamp': timestamp, 'X-Nonce': nonce };
}
const body = JSON.stringify({ consent_type: 'document_approval', decision: 'accepted', /* … */ });
const headers = {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
Authorization: `Bearer ${jwt}`, // مسارات S2S فقط
...sign(apiKey, 'POST', '/tool/v1/s2s/consents', body),
};

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

مسارات خادم إلى خادم (/tool/v1/s2s/…)

Section titled “مسارات خادم إلى خادم (/tool/v1/s2s/…)”

تحتاج طلبات S2S إلى ثلاثة أشياء: مفتاح API بصلاحية كتابة، وتوقيع الطلب، و JWT لمستخدم له الدور api_user:

  1. ادعُ عضوًا في الفريق بالدور api_user من لوحة التحكم. هذا الدور يستطيع استخدام S2S API فقط؛ ولا يستطيع تسجيل الدخول إلى اللوحة.
  2. سجّل الدخول عبر POST /api/v1/auth/login للحصول على JWT؛ وجدّده عبر POST /api/v1/auth/refresh.
  3. أرسل Authorization: Bearer <jwt> مع X-API-Key وترويسات التوقيع. يجب أن ينتمي المستخدم إلى المستأجر (tenant) الذي يملك الموقع.

طلبات S2S وحدها يجوز لها ضبط ip_address و user_agent و otp_verified و otp_reference. على مسار المتصفح يُؤخذ عنوان IP و user agent دائمًا من الاتصال، وتُتجاهل حقول OTP.


POST /tool/v1/consents (أو /tool/v1/s2s/consents)

{
"consent_type": "cookie",
"decision": "partial",
"document_version_id": "0190a1b2-…",
"language": "en",
"categories": { "necessary": true, "analytics": true, "marketing": false },
"idempotency_key": "9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"visitor_id": "user-4711",
"visitor_region": "DE",
"compliance_framework": "gdpr",
"metadata": { "order_id": "ORD-54321" }
}
الحقلالقواعد
consent_typeمطلوب. حتى 50 محرفًا (A–Z a–z 0–9 _ - .). المستخدَم حاليًا: cookie و document_approval و do_not_sell_opt_out.
decisionمطلوب. accepted أو rejected أو partial أو withdrawn.
document_version_idUUID لإصدار نشط من مستند مرفق بالموقع. بدونه — أو مع إصدار غير نشط — يُرفض الطلب بـ 409 CONFIG_OUTDATED. الموافقة على ملفات تعريف الارتباط تشير إلى مستند cookie_policy الخاص بالموقع.
languageإن أُعطيت، يجب أن تكون لغة ذلك الإصدار.
categoriesكائن من مفتاح الفئة ← قيمة منطقية. لا تُخزَّن إلا الفئات المعرَّفة في تكوين اللافتة.
idempotency_key8–64 محرفًا (A–Z a–z 0–9 . _ : -). انظر أدناه.
visitor_idمرجعك الخاص للشخص، حتى 100 محرف.
visitor_region, visitor_countryحتى 10 / 2 محارف.
compliance_frameworkkvkk أو gdpr أو uk_gdpr أو ccpa أو lgpd أو popia أو pipeda أو pipl أو pipa أو none.
sourceحتى 20 محرفًا، مثل client و server.
metadata, extra_fieldsكائنات بـ 30 مفتاحًا على الأكثر ونحو 16 KB لكل منها.
approval_tokenرمز مميز لمرة واحدة لموافقة من نوع document_approval (انظر أدناه).
حقول التوقيع / المستند المخصصsignature_image, signature_mime_type, signer_name, signer_email, signer_phone, signer_id_number, declaration_text, drawing_duration_ms, stroke_count, strokes_data, input_type, screen_resolution, custom_document_content, custom_document_mime_type — كلها في المستوى الأعلى. راجع الموافقة على المستندات والتوقيع.
ip_address, user_agent, otp_verified, otp_referenceS2S فقط.

احصل على document_version_id من GET /tool/v1/config?language=en (data.documents[].version.id) أو من GET /tool/v1/documents/{slug}/active?language=en.

{
"message": "…",
"data": {
"consent_token": "ct_…",
"proof_hash": "…64 hex characters…",
"created_at": "2026-10-10T10:00:00+00:00",
"duplicate": false
}
}

202 يعني أن الطلب اجتاز جميع الفحوص وأن السجل قُبل للكتابة. الرمز المميز وتجزئة الإثبات (proof hash) والتوقيت نهائية عند تلك النقطة، لكن الصف يكتبه عامل في الخلفية، ولذلك قد لا يجده status و verify للحظة قصيرة.

أرسل مفتاحًا واحدًا لكل إجراء من المستخدم وأعد استخدامه في كل إعادة محاولة لذلك الإجراء. الطلب المكرر بالمفتاح نفسه لا ينشئ سجلًا ثانيًا ولا يستهلك من الحصة؛ بل يعيد الرد الأصلي مع "duplicate": true. تُحفظ المفاتيح مدة 24 ساعة. حزمة المتصفح تولّد UUID واحدًا لكل قرار وتستخدمه في المحاولة الأولى، وفي إعادات المحاولة من جهة العميل، وفي إعادات الإرسال من قائمة انتظارها غير المتصلة.

POST /tool/v1/consents/batch مع { "consents": [ { … }, … ] } (من 1 إلى 100 عنصر، بالحقول نفسها المذكورة أعلاه).

{
"data": {
"accepted": 2,
"rejected": 1,
"results": [
{ "index": 0, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": false },
{ "index": 1, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": true },
{ "index": 2, "status": "rejected", "error": "…", "error_code": "CONSENT_LIMIT_EXCEEDED", "code": 429 }
]
}
}

تكون الحالة 202 عندما تُعالَج الدفعة؛ افحص كل مُدخل في results. أخطاء التحقق من الصحة، ووجود عنصر بإصدار مستند غير معروف أو غير نشط، ترفض الدفعة كلها (422 / 409).


الرموز المميزة للموافقة لمرة واحدة

Section titled “الرموز المميزة للموافقة لمرة واحدة”

POST /tool/v1/s2s/approval-tokens — S2S فقط.

{
"document_slug": "service-contract",
"subject": "user-4711",
"signer_name": "Jane Doe",
"signer_email": "[email protected]",
"expires_in": 600
}
الحقلالقواعد
document_slug / document_version_idواحد منهما على الأقل. مع معرّف إصدار يكون الرمز صالحًا لذلك الإصدار فقط.
subjectمطلوب. مرجعك الخاص للشخص (100 محرف كحد أقصى)؛ يُخزَّن بصفته visitor_id للسجل.
signer_name, signer_email, signer_phone, signer_id_numberاختياري. بيانات الهوية التي تشهد بها.
otp_verified, otp_referenceاختياري. صرّح بفحص OTP أجريته أنت.
custom_document_hashاختياري. SHA-256 (hex) لمستند أنشأه نظامك؛ ويجب عندئذ أن تحمل الموافقة ذلك المحتوى بالضبط.
expires_inبالثواني، 30–3600. الافتراضي 600.

الرد 201:

{
"data": {
"approval_token": "hat_…",
"approval_token_id": "atk_…",
"expires_in": 600,
"expires_at": "2026-10-10T10:10:00+00:00",
"document_id": "…",
"document_slug": "service-contract",
"document_version_id": null
}
}

أعطِ approval_token للصفحة ومرّره إلى الحزمة (approvalToken) أو أرسله في الحقل approval_token في المستوى الأعلى لموافقة من نوع document_approval. يستهلكه الخادم مرة واحدة، ويأخذ الهوية من الرمز، ويسجّل identity_assurance: tenant_attested مع القيمة غير السرية approval_token_id. الطلب الذي يُرفض لسبب آخر (حد المعدل، الحصة) لا يستهلك الرمز. لا يخزّن HashEntry إلا تجزئة للرمز.


GET /tool/v1/consents/status?visitor_id=…

Section titled “GET /tool/v1/consents/status?visitor_id=…”

يعيد ما يصل إلى 20 موافقة غير منتهية الصلاحية لقيمة visitor_id واحدة، الأحدث أولًا. عوامل تصفية اختيارية: consent_type و document_slug و document_version_id.

{id} هو UUID لموافقة، أو consent_token (ct_…)، أو proof_hash.

{
"valid": true,
"data": {
"consent_type": "cookie",
"decision": "accepted",
"proof_hash": "…",
"hash_version": 2,
"hash_valid": true,
"merkle_valid": null,
"is_anonymized": false,
"is_tsa_stamped": false,
"created_at": "2026-10-10T10:00:00+00:00"
}
}

ما يعنيه هذا الجواب — وما لا يعنيه:

  • valid: true يعني: السجل يخص مستأجرك، وقد أعاد HashEntry حساب تجزئة الإثبات من السجل المخزَّن بالمفتاح السري لمستأجرك فطابقت، و— إذا كان السجل جزءًا من دفعة Merkle — إثبات تضمينه صحيح. وجود صف لا يكفي.
  • merkle_valid يكون null عندما لا يكون للسجل إثبات Merkle.
  • is_tsa_stamped يفيد بوجود رمز ختم زمني (timestamp token) للسجل (مباشرةً أو عبر دفعة Merkle الخاصة به). ولا يفيد بأن توقيع الرمز جرى التحقق منه في هذا الاستدعاء.
  • هذا فحص يجريه HashEntry على بيانات HashEntry. للتحقق دون الوثوق بجواب الخادم، صدّر حزمة أدلة وتحقق منها بنفسك: التحقق من الأدلة دون اتصال.
  • المعرّف غير المعروف يعيد 404 مع "valid": false و code: "CONSENT_NOT_FOUND".

بلا مفتاح API؛ ومقيَّد بحد للمعدل. المعاملات: token (رمز ct_… أو قيمة ملف تعريف الارتباط _he_consent الخاص بالحزمة) أو proof_hash أو id. يعيد القرار والفئات ونطاق الموقع والتوقيتات وحالة التجزئة ومعلومات الختم الزمني، ولا يعيد أبدًا عنوان IP ولا user agent ولا معرّف الزائر.

  • سجل موجود: 200 مع valid كما هو موصوف أعلاه.
  • قيمة ملف تعريف ارتباط من المتصفح تتعذر مطابقتها مع سجل في الخادم: 200 مع "valid": false و "source": "client_token" و "server_record_found": false وحقول ملف تعريف الارتباط بعد فك ترميزها. ملف تعريف الارتباط غير موقَّع، ولذلك لا يُبلَّغ عنه أبدًا بأنه صالح بمفرده.
  • لم يُعثر على شيء: 404 مع "valid": false. بلا معرّف: 422.

ردود الأخطاء بصيغة JSON وتحمل رمزًا آليًا ثابتًا في code (وكذلك في error_code و error.code؛ وبعض أخطاء المصادقة تستخدم error.code فقط).

HTTPالرمزالمعنى
401MISSING_API_KEY, INVALID_API_KEYمفتاح API غائب أو غير معروف.
401MISSING_SIGNATURE_HEADERS, TIMESTAMP_EXPIRED, INVALID_SIGNATURE, INVALID_NONCE, NONCE_REPLAYEDمشكلات في توقيع الطلب.
401MISSING_BEARER_TOKEN, INVALID_BEARER_TOKENS2S: الـ JWT مفقود أو غير صالح.
403SITE_NOT_VERIFIEDوثّق الموقع في لوحة التحكم.
403ORIGIN_NOT_ALLOWED, IP_NOT_ALLOWEDليس ضمن قائمة المسموح بهم للموقع.
403INSUFFICIENT_ROLE, TENANT_MISMATCHS2S: المستخدم ليس api_user لهذا المستأجر.
403—نطاق صلاحية مفتاح API لا يسمح بهذه الطريقة.
404DOCUMENT_NOT_FOUND, VERSION_NOT_FOUND, CONSENT_NOT_FOUND
409CONFIG_OUTDATEDdocument_version_id مفقود، أو غير نشط، أو بلغة خاطئة، أو أن ذاكرة التكوين المؤقتة في الخادم باردة. أعد قراءة /tool/v1/config؛ ولا تُعد الإرسال إلا إذا كان الإصدار ما زال هو النشط.
422VALIDATION_ERRORأخطاء الحقول موجودة في errors.
422APPROVAL_TOKEN_REQUIRED, APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_CONFLICTراجع الرموز المميزة للموافقة لمرة واحدة.
422INVALID_CATEGORIES, METADATA_TOO_MANY_KEYS, METADATA_TOO_LARGE, EXTRA_FIELDS_TOO_MANY_KEYS, EXTRA_FIELD_NAME_TOO_LONG, EXTRA_FIELDS_TOO_LARGE, STROKES_DATA_TOO_LARGE, INVALID_SIGNATURE_IMAGE, SIGNATURE_IMAGE_TOO_LARGE, INVALID_CUSTOM_DOCUMENT, UNSUPPORTED_CUSTOM_DOCUMENT_TYPE, CUSTOM_DOCUMENT_TOO_LARGEحدود الحمولة.
429RATE_LIMIT_EXCEEDEDحد المعدل الخاص بالخطة. انظر Retry-After و X-RateLimit-Limit و X-RateLimit-Remaining.
429CONSENT_LIMIT_EXCEEDED, NO_CONSENT_SUBSCRIPTIONحصة الموافقات في الخطة استُنفدت / لا توجد خطة نشطة.
429TOO_MANY_INVALID_API_KEYSطلبات كثيرة جدًا بمفاتيح غير صالحة من عنوان IP واحد.
503QUEUE_OVERLOADED, ADMISSION_UNAVAILABLE, API_KEY_LOOKUP_UNAVAILABLEمؤقت. أعد المحاولة بعد Retry-After بالقيمة نفسها لـ idempotency_key.

أعد محاولة الردود 408 و 425 و 429 و 5xx بالجسم نفسه وبالقيمة نفسها لـ idempotency_key (وبتوقيت و nonce وتوقيع جديدة). لا تُعد محاولة ردود 4xx الأخرى دون تغيير.