Events API
تتبّع أحداث المستخدم وسلوكه برمجياً عبر REST API.
كل نقاط النهاية في هذه الصفحة نسبية إلى عنوان الأساس https://api-eu1.joryio.com. راجع نظرة عامة على API.
حدث الشراء
تقارير الإيرادات والإسناد وCLV والنماذج التنبؤية جميعها تقرأ حدثًا واحدًا. إذا أرسلت عمليات الشراء باسم مختلف، فلن تُحتسب في أي مكان.
| اسم الحدث | Order Completed |
| الخصائص المطلوبة | total, currency |
| موصى بها | total_base, base_currency, orderId |
{
"eventName": "Order Completed",
"userId": "user_123",
"properties": {
"total": 149.90,
"currency": "EUR",
"total_base": 162.35,
"base_currency": "USD",
"orderId": "1024"
}
}
لماذا اسم واحد فقط
تستخدم منصات أخرى أسماء أخرى - Placed Order (Klaviyo) وpurchase (GA4).
نحن لا نقبلها عمدًا، لأن قبول عدة أسماء يعني جمعها معًا: متجر يشغّل وسم GA4
إلى جانب موصّلنا سيرسل عملية بيع واحدة باسمين ويرى إيراداته مضاعفة. رقم إيرادات
خاطئ بمقدار الضعف أصعب بكثير في الاكتشاف من رقم يساوي صفرًا بوضوح.
لذلك القاعدة هي عقد واحد منشور. إذا لم تظهر الطلبات، يكون السبب واضحًا فورًا عند الإعداد - وقابلًا للإصلاح - بدلًا من أن يكون خاطئًا بصمت لأشهر.
عن المبالغ
total وtotal_base رقمان مختلفان، وليسا بديلين:
total- ما دفعه العميل بالعملة التي دفع بها.total_base+base_currency- الطلب نفسه محوّلًا إلى عملة تقاريرك. أرسلهما إذا كنت تبيع بأكثر من عملة، لتُجمع الإجماليات بشكل صحيح.
أرسل total وحده إذا كانت لديك عملة واحدة. orderId اختياري لكنه موصى به:
فهو يمنع ازدواج طلب يصل أكثر من مرة (إعادة محاولة، تحديث صفحة الدفع).
إذا كنت ترسل بالفعل اسمًا آخر
تُحفظ أحداثك السابقة، لكنها لا تُعامل كطلبات. حوّل الطلبات الجديدة إلى
Order Completed، وتبدأ تقارير الإيرادات من تلك النقطة. نحن لا نعيد كتابة
الأحداث القديمة، لذا لا يُعاد تفسير أي شيء بصمت.
المصادقة
تتطلب جميع الطلبات مصادقة بمفتاح API:
Authorization: Bearer jry_live_your_api_key_here
Content-Type: application/json
تتبع حدث
تتبّع حدثاً واحداً لمستخدم مع خصائص اختيارية.
تقبل نقطة النهاية هذه شكلين للنص: كائن حدث واحد، الموثق هنا، أو مصفوفة JSON مجردة من كائنات الأحداث للتتبع على دفعات، بحد أقصى 500. راجع تتبع أحداث متعددة، نص مصفوفة.
نقطة النهاية
POST /events/track
نص الطلب
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
userId | string | نعم* | معرّف المستخدم لديك. يلزم واحد من userId أو joryioUserId أو anonymousId أو userAlias |
eventName | string | نعم | اسم الحدث، بحد أقصى 255 حرفاً |
properties | object | لا | خصائص الحدث، 200 مفتاح علوي كحد أقصى، 50KB وعمق تداخل 5 |
timestamp | string أو number | لا | وقت الحدث؛ سلسلة ISO 8601 أو ميلي ثانية epoch، والافتراضي الآن |
joryioUserId | string | لا | معرّف المستخدم الداخلي في Joryio، وهو id سداسي من 24 حرفاً في استجابات Users API، بديل لـ userId |
anonymousId | string | لا | معرّف زائر مجهول، بديل |
userAlias | object | لا | { aliasLabel, aliasName }، معرّف alias بديل لـ userId |
sessionId | string | لا | معرّف الجلسة |
deviceId | string | لا | معرّف الجهاز |
clientEventId | string | لا | معرّف حدث يولده العميل؛ يستخدم كمعرّف الحدث المخزن، فتزال تكرارات إعادة المحاولة بالقيمة نفسها |
مثال لطلب
curl -X POST https://api-eu1.joryio.com/events/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_123",
"eventName": "Order Completed",
"properties": {
"orderId": "order_456",
"total": 99.99,
"currency": "USD",
"items": 3,
"paymentMethod": "credit_card"
},
"timestamp": "2024-01-20T14:30:00.000Z"
}'
الاستجابة
يعيد شكل الكائن الواحد معرّف الحدث المخزن:
{
"eventId": "9b2f6c1e-4a8d-4f0b-9c3d-2e1f5a6b7c8d",
"success": true
}
ملاحظات
- تعالج الأحداث بصورة غير متزامنة.
- استخدم تسمية أحداث متسقة. راجع أفضل ممارسات تسمية الأحداث.
- تُفهرس الخصائص لاستخدامها في التقسيم.
- يستخدم الطابع الزمني وقت الخادم إن لم تقدمه.
تتبع أحداث متعددة، نص مصفوفة
لا توجد نقطة نهاية دفعات منفصلة: يقبل POST /events/track إما كائن حدث واحداً أو مصفوفة JSON مجردة من كائنات الأحداث، بلا كائن غلاف. يتتبع شكل المصفوفة حتى 500 حدث في طلب واحد.
نقطة النهاية
POST /events/track
نص الطلب
مصفوفة JSON بحد أقصى 500 عنصر. يتبع كل عنصر تنسيق الكائن الواحد نفسه.
يتحقق النظام من كل عنصر بالكامل. يُبلغ عن العنصر غير الصالح في rejected بفهرسه في المصفوفة، ولا يُقبل بصمت، في حين تستمر معالجة العناصر الصالحة الأخرى.
مثال لطلب
curl -X POST https://api-eu1.joryio.com/events/track \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '[
{
"userId": "user_123",
"eventName": "Product Viewed",
"properties": {
"productId": "prod_456",
"price": 49.99
}
},
{
"userId": "user_123",
"eventName": "Added To Cart",
"properties": {
"productId": "prod_456",
"quantity": 1
}
},
{
"userId": "user_456",
"eventName": "Page Viewed",
"properties": {
"page": "/pricing"
}
}
]'
الاستجابة
بخلاف شكل الكائن، الذي يعيد { eventId, success }، يعيد شكل المصفوفة ملخصاً مجمعاً:
{
"processed": 3,
"accepted": 3,
"rejected": []
}
| الحقل | الوصف |
|---|---|
processed | عدد العناصر التي استقبلتها مصفوفة الطلب |
accepted | الأحداث المسجلة فعلياً |
rejected | فشل لكل عنصر: index موضعه في مصفوفة الطلب وname اسم الحدث إن وجد وreason السبب |
مثال لعنصر واحد غير صالح:
{
"processed": 3,
"accepted": 2,
"rejected": [
{
"index": 1,
"name": "Added To Cart",
"reason": "property hacker should not exist"
}
]
}
الحدود
- 500 حدث كحد أقصى في الطلب؛ يعيد الزائد
400، كما تعيد المصفوفة الفارغة400. - يتبع كل حدث تنسيق الكائن الواحد.
- معالجة الدفعة ليست ذرية: تُسجل الأحداث الصالحة حتى عند رفض بعض العناصر. افحص
rejectedللفشل الجزئي.
الاستعلام عن الأحداث
استرجع الأحداث بالتصفية والترقيم. لا توجد نقطة نهاية لجلب حدث بمعرّفه؛ صفِّ هذا الاستعلام بدلاً منها.
نقطة النهاية
GET /events/query
معاملات الاستعلام
| المعامل | النوع | الافتراضي | الوصف |
|---|---|---|---|
userId | string | - | تصفية بمعرّف المستخدم |
eventName | string | - | تصفية باسم الحدث |
startDate | string | - | الأحداث بعد هذا التاريخ، ISO 8601 |
endDate | string | - | الأحداث قبل هذا التاريخ، ISO 8601 |
limit | number | 100 | النتائج في الصفحة، بحد أقصى 1000 |
offset | number | 0 | عدد الأحداث التي يجب تجاوزها |
مثال لطلب
# Get all "Order Completed" events in January 2024
curl -X GET "https://api-eu1.joryio.com/events/query?eventName=Order+Completed&startDate=2024-01-01T00:00:00Z&endDate=2024-02-01T00:00:00Z&limit=100" \
-H "Authorization: Bearer jry_live_your_api_key"
الاستجابة
مصفوفة JSON مجردة، الأحدث أولاً. تستخدم حقول الحدث snake_case لأنها من مخزن التحليلات:
[
{
"event_id": "9b2f6c1e-4a8d-4f0b-9c3d-2e1f5a6b7c8d",
"user_id": "665f1e2a9b3c4d5e6f7a8b9c",
"anonymous_id": "",
"event_name": "Order Completed",
"properties": {
"orderId": "order_456",
"total": 99.99
},
"timestamp": "2024-01-20 14:30:00",
"session_id": "",
"device_id": ""
}
]
تجميع الأحداث
احصل على إحصاءات أحداث مجمعة بالتجميع والمقاييس.
نقطة النهاية
POST /events/aggregate
نص الطلب
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
eventName | string | نعم | اسم الحدث المراد تجميعه |
startDate | string | لا | تاريخ البدء، ISO 8601 |
endDate | string | لا | تاريخ النهاية، ISO 8601 |
groupBy | string | لا | تجميع زمني: hour أو day أو week أو month، الافتراضي day |
metrics | array | لا | المقاييس: count وsum وavg وmin وmax، الافتراضي ['count'] |
sumField | string | لا | اسم مفتاح properties للجمع أو التجميع، مثل total |
avgField | string | لا | اسم مفتاح properties لحساب المتوسط |
eventProperties | object | لا | تصفية بخصائص الحدث |
مثال لطلب
curl -X POST https://api-eu1.joryio.com/events/aggregate \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"eventName": "Order Completed",
"startDate": "2024-01-01T00:00:00Z",
"endDate": "2024-02-01T00:00:00Z",
"groupBy": "day",
"metrics": ["count", "sum"],
"sumField": "total"
}'
الاستجابة
{
"success": true,
"data": {
"eventName": "Order Completed",
"groupBy": "day",
"metrics": ["count", "sum"],
"results": [
{
"period": "2024-01-01T00:00:00Z",
"count": 45,
"sum_value": 4567.89
},
{
"period": "2024-01-02T00:00:00Z",
"count": 52,
"sum_value": 5123.45
}
],
"total": 2
}
}
المقاييس المدعومة
- count: العدد الإجمالي للأحداث.
- sum: مجموع قيم الحقل المحدد.
- avg: متوسط قيم الحقل المحدد.
- min: أدنى قيمة للحقل المحدد.
- max: أعلى قيمة للحقل المحدد.
مثال: تحليل الإيرادات
# Get daily revenue from purchases
curl -X POST https://api-eu1.joryio.com/events/aggregate \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"eventName": "Order Completed",
"startDate": "2024-01-01T00:00:00Z",
"endDate": "2024-01-31T00:00:00Z",
"groupBy": "day",
"metrics": ["count", "sum", "avg"],
"sumField": "total"
}'
مثال: استخدام ميزة بحسب الساعة
# Track feature usage patterns by hour
curl -X POST https://api-eu1.joryio.com/events/aggregate \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"eventName": "Feature Used",
"startDate": "2024-01-20T00:00:00Z",
"endDate": "2024-01-21T00:00:00Z",
"groupBy": "hour",
"metrics": ["count"]
}'
أحداث شائعة
أحداث التجارة الإلكترونية
// Product Viewed
POST /events/track
{
"userId": "user_123",
"eventName": "Product Viewed",
"properties": {
"productId": "prod_456",
"productName": "Premium Plan",
"category": "Subscription",
"price": 99.99,
"currency": "USD"
}
}
// Added To Cart
POST /events/track
{
"userId": "user_123",
"eventName": "Added To Cart",
"properties": {
"productId": "prod_456",
"quantity": 1,
"price": 99.99
}
}
// Order Completed
POST /events/track
{
"userId": "user_123",
"eventName": "Order Completed",
"properties": {
"orderId": "order_789",
"total": 249.99,
"currency": "USD",
"itemCount": 3,
"discount": 25.00,
"paymentMethod": "credit_card"
}
}
أحداث دورة حياة المستخدم
// Signup Completed
POST /events/track
{
"userId": "user_123",
"eventName": "Signup Completed",
"properties": {
"method": "email",
"source": "homepage_cta"
}
}
// Onboarding Completed
POST /events/track
{
"userId": "user_123",
"eventName": "Onboarding Completed",
"properties": {
"stepsCompleted": 5,
"timeSpent": "8m 30s"
}
}
// Trial Started
POST /events/track
{
"userId": "user_123",
"eventName": "Trial Started",
"properties": {
"plan": "premium",
"trialDays": 14
}
}
أحداث التفاعل
// Feature Used
POST /events/track
{
"userId": "user_123",
"eventName": "Feature Used",
"properties": {
"featureName": "export",
"exportFormat": "csv",
"recordCount": 1500
}
}
// Page Viewed
POST /events/track
{
"userId": "user_123",
"eventName": "Page Viewed",
"properties": {
"page": "/pricing",
"category": "Marketing",
"referrer": "google"
}
}
خصائص الحدث
أفضل الممارسات
استخدم أسماء خصائص وصفية.
جيد:
{
"properties": {
"productId": "prod_123",
"productName": "Premium Plan",
"price": 99.99,
"currency": "USD"
}
}
غير جيد:
{
"properties": {
"pid": "prod_123",
"n": "Premium Plan",
"p": 99.99
}
}
أنواع البيانات المدعومة
{
"properties": {
"string": "value",
"number": 99.99,
"integer": 5,
"boolean": true,
"date": "2024-01-15T10:30:00Z",
"array": ["tag1", "tag2"],
"object": {
"nested": "value",
"deep": {
"property": "value"
}
}
}
}
الخصائص المحجوزة
الخصائص التي تبدأ بـ $ محجوزة للنظام:
$app_id- معرّف التطبيق.$app_name- اسم التطبيق.$platform- المنصة، web أو ios أو android.$session_id- معرّف الجلسة.$anonymous_id- معرّف المستخدم المجهول.
لا تستخدم هذه الأسماء لخصائص مخصصة.
حدود الأحداث
حدود الحجم
| الحد | القيمة |
|---|---|
| أقصى طول لاسم الحدث | 255 حرفاً |
| أقصى طول للمعرّف، userId وanonymousId وsessionId وdeviceId وclientEventId | 255 حرفاً |
| أقصى مفاتيح خصائص عليا لكل حدث | 200 |
| أقصى حجم للخصائص، JSON | 50 KB |
| أقصى عمق لتداخل الخصائص | 5 مستويات |
| أقصى أحداث في طلب نص مصفوفة | 500 |
حدود المعدل
لا تملك Events API اليوم حدود معدل ثابتة لكل نقطة نهاية. راجع نظرة API العامة: حدود المعدل.
استجابات الأخطاء
تستخدم كل الأخطاء نص الخطأ القياسي. راجع نظرة API العامة: استجابة الخطأ.
400 طلب غير صالح: فشل التحقق
{
"statusCode": 400,
"message": "Bad Request Exception",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/events/track",
"errors": [
"eventName should not be empty"
]
}
400 طلب غير صالح: خصائص ضخمة
{
"statusCode": 400,
"message": "Event properties exceed maximum size of 50KB (received 63KB)",
"timestamp": "2026-01-15T10:30:00.000Z",
"path": "/events/track"
}
أفضل الممارسات
1. اجمع الأحداث عند الإمكان
جيد: أرسل أحداثاً متعددة بنص مصفوفة.
await fetch('/events/track', {
method: 'POST',
body: JSON.stringify([event1, event2, event3])
});
غير جيد: طلبات منفردة.
await fetch('/events/track', { method: 'POST', body: JSON.stringify(event1) });
await fetch('/events/track', { method: 'POST', body: JSON.stringify(event2) });
await fetch('/events/track', { method: 'POST', body: JSON.stringify(event3) });
2. استخدم تسمية أحداث متسقة
اتبع نمط «كائن + فعل بصيغة الماضي»:
جيد: Product Viewed وOrder Completed وTrial Started.
غير جيد: view_product وclicked وuser_action_123.
3. ضمّن طوابع زمنية
للأحداث التاريخية، ضمّن وقتاً دقيقاً دائماً:
{
"userId": "user_123",
"eventName": "Order Completed",
"timestamp": "2024-01-15T10:30:00.000Z", // Actual event time
"properties": { ... }
}
4. أبقِ الخصائص مختصرة
ضمّن الخصائص ذات الصلة فقط.
جيد:
{
"eventName": "Order Completed",
"properties": {
"orderId": "order_123",
"total": 99.99,
"currency": "USD"
}
}
غير جيد:
{
"eventName": "Order Completed",
"properties": {
"orderId": "order_123",
"total": 99.99,
"currency": "USD",
"userAgent": "Mozilla/5.0...", // Too much detail
"sessionData": { /* large object */ },
"cookies": [ /* array of cookies */ ]
}
}
5. تعامل مع الأخطاء بأمان
نفذ منطق إعادة المحاولة بتراجع أسي:
async function trackWithRetry(event, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch('/events/track', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(event)
});
if (response.ok) return await response.json();
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After') || Math.pow(2, i);
await sleep(retryAfter * 1000);
continue;
}
throw new Error(`HTTP ${response.status}`);
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(Math.pow(2, i) * 1000); // 1s, 2s, 4s
}
}
}
التصحيح
تفعيل وضع التصحيح في SDK
عند استخدام Web SDK:
import JoryioSDK from '@joryio/web-sdk';
const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_...',
enableDebug: true // Log all events to console
});
التحقق من الأحداث في لوحة التحكم
- انتقل إلى المستخدمون وابحث عن المستخدم.
- انقر تبويب النشاط.
- راجع جميع الأحداث المتتبعة.
مشاكل شائعة
الأحداث لا تظهر:
- تحقق من صحة مفتاح API.
- تحقق من تعريف المستخدم.
- تأكد من صحة اسم الحدث وخصائصه.
- تحقق من حدود المعدل.
الخصائص لا تظهر:
- تحقق من صحة أسماء الخصائص.
- تحقق من دعم أنواع البيانات.
- تجنب أسماء الخصائص المحجوزة التي تبدأ بـ
$.
SDKs
للتكامل الأسهل، استخدم SDKs الرسمية:
- Web SDK: دليل التكامل
- iOS SDK: Swift Package / CocoaPods
- Android SDK: Gradle
- React Native SDK: npm install @joryio/react-native-sdk