يتيح Server API لخادمك الخلفي أن يقرأ بيانات التعرّف التي جمعها TRACIO بالفعل لمساحة عملك: تاريخ الزائر، والجلسات المفردة، وعدّادات velocity على نافذة قصيرة.
وهو مكمّل لـ Webhooks لا بديل عنها:
| Webhooks | Server API | |
|---|---|---|
| الاتجاه | يدفع TRACIO إلى نقطة النهاية لديك | يسحب خادمك الخلفي عند الطلب |
| التوقيت | مع وقوع كل عملية تعرّف | في أي وقت، ضمن نافذة الاحتفاظ لديك |
| الأنسب لـ | التفاعل مع حدث | البحث عن البيانات أثناء اتخاذ قرار، وإعادة التعبئة، والتحقيقات |
كلا السطحين متاح اعتبارًا من خطة Pro وما فوقها.
https://api.tracio.ai/v1هذا مضيف مختلف عن نقطة نهاية المتصفح (edge.tracio.ai) وعن لوحة التحكم
(app.tracio.ai). والثلاثة منفصلة: يتحدث المتصفح إلى الحافة بمفتاحك العام، ويتحدث
خادمك الخلفي إلى Server API بمفتاحك السرّي.
يحمل كل طلب مفتاحك السرّي بوصفه رمز bearer:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"Server API مخصّص للاتصال بين الخوادم فقط. فترويسات CORS لا تُعاد عن قصد، ولذلك لا يستطيع المتصفح استدعاءه — وهذا هو ما يُبقي مفتاحك السرّي خارج الكود الذي يعمل من جهة العميل. لا تشحن المفتاح السرّي إلى المتصفح أبدًا.
أنشئه في لوحة التحكم ضمن API Keys، واختر النوع secret.
tracio_sk_ متبوعًا بـ 43 حرفًا، أي 53 حرفًا في المجموع.
وتسرده لوحة التحكم بأحرفه الأولى القليلة لتتمكّن من التمييز بين المفاتيح.يُصدر التدوير مفتاحًا جديدًا ويُبقي القديم صالحًا لمدة 7 أيام، فيمكنك طرحه دون توقّف للخدمة. انشر المفتاح الجديد، وتأكّد من انتقال حركة الطلبات إليه، ثم دع القديم ينتهي. أما المفاتيح العامة فغير قابلة للتدوير — فهي ليست أسرارًا وهي ظاهرة في شفرة صفحتك عن قصد.
كل مسار هو GET. ولا توجد عمليات كتابة في Server API: فهو يقرأ البيانات، أما إعداداتك
فتعيش في لوحة التحكم.
| الطريقة | المسار | ما يعيده |
|---|---|---|
GET | /v1/visitors/{visitorId} | تاريخ مجمَّع لزائر واحد، إضافةً إلى أحدث جلسة له |
GET | /v1/visitors/{visitorId}/sessions | قائمة مقسَّمة إلى صفحات بجلسات ذلك الزائر |
GET | /v1/visitors/{visitorId}/sessions/latest | الجلسة الأحدث وحدها |
GET | /v1/visitors/{visitorId}/velocity | عدّادات النشاط على نافذة قصيرة |
GET | /v1/sessions/{requestId} | جلسة واحدة بمعرّف طلبها |
GET | /.well-known/webhook-keys | المفاتيح العامة لتوقيع منصة الـ webhook (بلا مصادقة) |
تُقبل الشرطة المائلة في نهاية المسار ويجري تجاهلها. أما المسار غير المعروف أو الطريقة الخاطئة فيعيدان مغلّف أخطاء JSON نفسه الذي يعيده كل شيء آخر، لا صفحة HTML ولا نصًّا عاديًا أبدًا.
كل قراءة محدودة بنافذة زمنية يضبطها معامِلا استعلام اختياريان:
| المعامِل | يقبل |
|---|---|
from | YYYY-MM-DD أو طابع وقت كامل بصيغة RFC 3339 |
to | YYYY-MM-DD أو طابع وقت كامل بصيغة RFC 3339 |
to يشمل ذلك اليوم بأكمله.400 invalid_request وبالرسالة
time must be YYYY-MM-DD or RFC3339.meta، لذا راجع meta.from
وmeta.to بدل افتراض أن طلبك نُفِّذ حرفيًا.curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"تحمل الاستجابة التاريخ المجمَّع وتُضمّن أحدث جلسة، فتكفي الحالةَ الشائعة طلبٌ واحد بدل طلبين:
{ "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "firstSeenAt": "2026-05-02T10:11:12Z", "lastSeenAt": "2026-07-25T08:00:00Z", "visits": 42, "incognitoVisits": 3, "uniqueIps": 5, "uniqueCountries": 2, "browsers": ["Chrome"], "os": ["macOS"], "devices": ["desktop"], "risk": { "maxRiskScore": 63, "avgBotScore": 12.5, "botSessions": 7, "lastDecision": "real" }, "network": { "vpnSeen": false, "proxySeen": false, "torSeen": false, "datacenterSeen": true, "lastIsp": "Deutsche Telekom" }, "lastSession": { "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "accountId": "user_8842", "timestamp": "2026-07-25T08:00:00Z", "tag": "checkout", "url": "https://shop.example.com/checkout", "ip": "203.0.113.42", "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", "browser": { "name": "Chrome", "version": "126.0" }, "os": { "name": "macOS", "version": "14.5" }, "device": "desktop", "geo": { "country": "DE", "city": "Berlin", "timezone": "Europe/Berlin", "isp": "Deutsche Telekom" }, "network": { "vpn": false, "proxy": false, "tor": false, "datacenter": false, "asn": 3320 }, "bot": { "result": "human", "score": 4.5, "antidetectScore": 2.1 }, "identification": { "confidence": 0.97, "incognito": false, "matchType": "exact", "matchConfidence": 0.99 }, "decision": { "action": "real", "riskScore": 12 } }, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}| الحقل | المعنى |
|---|---|
visits، incognitoVisits | إجمالي الزيارات في النافذة، وكم منها جرى في نافذة تصفّح خاصة |
uniqueIps، uniqueCountries | العناوين والبلدان المختلفة التي رُصدت في النافذة |
browsers، os، devices | البيئات المختلفة التي ظهر فيها هذا الزائر |
risk.maxRiskScore | أعلى درجة مخاطر سُجّلت في النافذة، 0..100 |
risk.lastDecision | الحكم المسجَّل لأحدث زيارة |
risk.avgBotScore، risk.botSessions | متوسط درجة البوت وعدد جلسات البوتات — Business وما فوقها |
network.*Seen | هل رُصد لهذا الزائر يومًا VPN أو بروكسي أو عقدة خروج Tor أو عنوان مركز بيانات |
network.lastIsp | أحدث مزوّد خدمة إنترنت — Business وما فوقها |
lastSession | كائن الجلسة الكامل لأحدث زيارة |
meta | الخطة، ومدّة احتفاظها بالأيام، والنافذة المطبَّقة فعليًا |
الزائر الذي لا توجد له بيانات داخل نافذة الاحتفاظ يعيد 404 not_found مع الرسالة
visitor not found in the retention window — وهذا ليس خطأً في تكاملك، بل يعني أن
الزائر جديد أو أن بياناته تجاوزت مدّة الاحتفاظ.
تحمل الجلسة حكمَين اثنين، وهما يجيبان عن سؤالين مختلفين — هل كان العميل مؤتمتًا، وإلامَ انتهى محرّك المخاطر إجمالًا:
| الحقل | القيم |
|---|---|
bot.result | human، bot، uncertain |
decision.action | real، fake، suspicious |
يقع كل من bot.score وdecision.riskScore على المقياس 0..100. واعتبارًا من Business
وما فوقها، يحوّلهما guidance إلى توصية لكل سيناريو على السلّم
allow ← challenge ← review ← deny — راجع
Guidance لمعرفة معنى كل درجة.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions?limit=100&minRiskScore=50"| المعامِل | الافتراضي | ملاحظات |
|---|---|---|
limit | 50 | بحدّ أقصى 500؛ والقيمة الأكبر تُقلَّم ولا تُرفض |
from، to | احتفاظ الخطة | النافذة الزمنية المشتركة الموصوفة أعلاه |
cursor | — | مؤشّر تصفّح معتم من الصفحة السابقة |
botResult | — | أبقِ الجلسات ذات هذا الحكم بشأن البوت فقط |
minRiskScore | — | أبقِ الجلسات عند درجة المخاطر هذه أو فوقها فقط، 0..100 |
تعود الجلسات بدءًا من الأحدث:
{ "items": [ { "requestId": "9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31", "visitorId": "X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y", "timestamp": "2026-07-25T08:00:00Z" } ], "nextCursor": "MTcyMTg5NDQwMDAwMDphYmMxMjM", "hasMore": true, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-04-26T00:00:00Z", "to": "2026-07-25T12:00:00Z" }}التصفّح قائم على المؤشّر. ولا يوجد معامِل page ولا offset: مرِّر قيمة nextCursor
التي استلمتها بوصفها cursor، وواصِل ما دامت hasMore صحيحة.
async function allSessions(visitorId: string, secretKey: string) { const sessions = [] let cursor: string | undefined
do { const url = new URL(`https://api.tracio.ai/v1/visitors/${visitorId}/sessions`) url.searchParams.set("limit", "500") if (cursor) url.searchParams.set("cursor", cursor)
const res = await fetch(url, { headers: { Authorization: `Bearer ${secretKey}` } }) if (!res.ok) throw new Error(`Server API: ${res.status}`)
const page = await res.json() sessions.push(...page.items) cursor = page.nextCursor } while (cursor)
return sessions}تعامَل مع المؤشّر بوصفه معتمًا — فمحتواه تفصيل تنفيذي وقد يتغيّر. والمؤشّر الذي جرى
تعديله يُرفض بالرمز 400 invalid_request وبالرسالة malformed cursor.
الجلسة الأحدث:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/sessions/latest"يعيد هذا كائن جلسة مجرّدًا — لا مصفوفة ولا مغلَّفًا داخل غلاف. والزائر الذي لا جلسات له
في النافذة يعيد 404 not_found مع
no sessions for this visitor in the retention window.
أو عبر requestId، وهو المعرّف الذي يظهر أيضًا في حمولة الـ webhook:
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/sessions/9c1f6a2e-3b7d-4c58-a1e2-6f0d8b4a7c31?visitorId=X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y"visitorId اختياري هنا، لكن تمريره حين تعرفه يجعل البحث أسرع بوضوح.
يجيب velocity عن سؤال «كم كان نشاط هذا الزائر مؤخرًا» — وهو شكل حشو بيانات الاعتماد واختبار البطاقات والتسجيلات بالجملة.
curl -H "Authorization: Bearer tracio_sk_XXXX...XXXX" \ "https://api.tracio.ai/v1/visitors/X7fh2Hg9LkMn3pQr5tBvQw3xZa9mK2pL4nR8dT6y/velocity?window=1h"يقبل window القيم 1h أو 24h أو 7d وقيمته الافتراضية 24h. وأي قيمة أخرى تُرفض
بالرمز 400 invalid_request وبالرسالة window must be one of: 1h, 24h, 7d.
{ "window": "1h", "events": 37, "uniqueIps": 9, "uniqueCountries": 3, "uniqueAccounts": 12, "botEvents": 4, "meta": { "plan": "business", "retentionDays": 90, "from": "2026-07-25T11:00:00Z", "to": "2026-07-25T12:00:00Z" }}يعدّ uniqueAccounts قيم linkedId المختلفة التي أرسلتها لهذا الجهاز — راجع
ربط الحسابات. وbotEvents متاح في Business وما فوقها.
الحقل الغائب يعني «لا بيانات»، لا صفرًا أبدًا. فالحقول التي لا قيمة لها تُحذف
بالكامل بدل أن تُرسَل بالقيمة 0 أو "" أو null: فالزائر الجديد تمامًا لا
matchConfidence له، والزيارة النظيفة لا antidetectScore لها ولا suspectScore.
والاستثناء الوحيد المقصود هو bot.score، فهو حاضر دائمًا حتى حين يساوي صفرًا. اقرأ
الحقول بحذر.
تعتمد الحمولة على خطتك. كل خطة فيها وصول إلى API تحصل على الجلسة الأساسية —
المعرّفات، والطابع الزمني، وعنوان URL، وعنوان IP، ووكيل المستخدم، والمتصفح، ونظام
التشغيل، والجهاز، والموقع الجغرافي، والشبكة، والبوت، والتعرّف، والحكم. وتضيف Pro الحقول
identification.matchType وidentification.matchConfidence وbot.antidetectScore.
وتضيف Business وEnterprise الحقول geo.isp وnetwork.asn وdecision.suspectScore
وidentification.driftScore وreasons وbehavior وguidance وdeviceInfo وحقول
مستوى الشخص (personId وreputation وlinkedAccountsCount وlinkedVisitorsCount).
وغياب حقل خاص بـ Business في خطة Pro ليس خطأً.
لا تُعاد أبدًا التفاصيل الداخلية على مستوى الإشارات، في أي خطة: فأسماء الإشارات المفردة وأوزانها والعتبات الكامنة خلف الحكم وقيم الإشارات الخام وتفصيل الدرجات تبقى لدينا. فالدرجة التي يمكن ردّها بالهندسة العكسية إلى مدخلاتها تكفّ عن كونها دفاعًا مفيدًا.
يستخدم كل إخفاق مغلَّفًا واحدًا:
{ "error": { "code": "unauthorized", "message": "missing Authorization: Bearer <secret key>", "requestId": "8f14e45fceea167a5a36dedd" }}requestId هذا ليس معرّف الزيارة. فقيمتان مختلفتان تتشاركان الاسم نفسه: داخل حمولة
الجلسة يكون requestId هو UUID الزيارة، وهو نفسه الذي يسلّمه الـ webhook؛ أما داخل
مغلَّف الخطأ فهو معرّف تتبّع من 24 حرفًا يُسكّ لكل استدعاء HTTP. ويعود معرّف التتبّع
أيضًا في ترويسة X-Request-Id مع كل استجابة، ناجحة كانت أم لا. أدرِجه حين تتواصل مع
الدعم — فهو الطريقة التي نعثر بها على استدعائك بالضبط.
| HTTP | code | المعنى |
|---|---|---|
| 400 | invalid_request | معامِل مفقود أو مشوَّه |
| 401 | unauthorized | المفتاح غائب أو غير صالح أو مُبطَل أو منتهي الصلاحية |
| 402 | upgrade_required | خطتك لا تشمل الوصول إلى API |
| 404 | not_found | لا شيء طابق داخل نافذة الاحتفاظ |
| 405 | method_not_allowed | المسار موجود، لكن ليس لتلك الطريقة |
| 429 | rate_limited | تجاوُز عدد الطلبات في الثانية أو تجاوُز الحصة اليومية |
| 500 | internal | أخفق شيء ما لدينا |
| 503 | unavailable | مخزن داعم غير قابل للوصول مؤقتًا |
تجري الفحوصات بترتيب ثابت — المفتاح، ثم الخطة، ثم الحدود — فالطلب بمفتاح خاطئ يُبلِّغ عن المفتاح أولًا دائمًا، لا عن مشكلة في الحصة.
تُقرأ حالتا 401 بصيغتين مختلفتين عن قصد: missing Authorization: Bearer <secret key>
تعني أن الترويسة لم تصل إطلاقًا، بينما invalid or revoked API key تعني أنها وصلت ولم
تطابق. أما 402 فتحمل Data API requires the Pro plan or higher.
تحمل كل استجابة مُصادَق عليها وضعك الحالي:
| الترويسة | المعنى |
|---|---|
X-RateLimit-Limit | حصتك اليومية |
X-RateLimit-Remaining | الاستدعاءات المتبقية اليوم |
X-RateLimit-Reset | وقت يونكس لإعادة الضبط — منتصف الليل بتوقيت UTC |
Retry-After | الثواني الواجب انتظارها، تُرسَل مع 429 فقط |
| الخطة | طلبات في الثانية | طلبات في اليوم | عمق التاريخ |
|---|---|---|---|
| Free | لا وصول إلى API | — | 7 أيام |
| Pro | 10 | 10,000 | 30 يومًا |
| Business | 50 | 100,000 | 90 يومًا |
| Enterprise | 200 | بلا عدّاد | 365 يومًا |