# ردیابی سمت سرور

بعضی رویدادها هیچ وقت در مرورگر اتفاق نمی‌افتند — تایید پرداخت که از درگاه به سرور شما می‌آید، تمدید خودکار، سفارشی که پشتیبانی ثبت می‌کند. بک‌اند شما این‌ها را با یک کلید سرور مستقیم می‌فرستد: همان مدل داده، همان هویت، همان گزارش‌ها.

## کی سراغ سمت سرور بروید

تگ مرورگر چیزی را می‌بیند که در صفحه اتفاق می‌افتد. بخشی از واقعیت کسب‌وکار شما بیرون از صفحه می‌گذرد: درگاه پرداخت نتیجه را به سرور شما اعلام می‌کند، اشتراک شب هنگام تمدید می‌شود، اپراتور پشتیبانی سفارش را دستی ثبت می‌کند، یا کاربر پیش از رسیدن پاسخ درگاه مرورگر را می‌بندد.

| موضوع | تگ مرورگر | ارسال سمت سرور |
| --- | --- | --- |
| بازدید صفحه، پیمایش، تعامل | بله | نه |
| افزودن به سبد، شروع تسویه | بله | ممکن، ولی معمولا لازم نیست |
| خرید تاییدشده توسط درگاه | ناقص و قابل اتکا نیست | بله |
| تمدید، صورت حساب دوره‌ای، بازگشت وجه | نه | بله |
| سفارشی که اپراتور ثبت می‌کند | نه | بله |
| رویداد پشت مسدودکننده تبلیغات | از دست می‌رود | بله |

هر دو مسیر به یک جدول رویداد می‌نویسند و یک گراف هویت را تغذیه می‌کنند. لازم نیست یکی را به نفع دیگری کنار بگذارید؛ اما یک رویداد را از هر دو مسیر نفرستید، چون شناسه‌های آن دو مسیر یکی نیستند و دو ردیف مستقل می‌شوند.

> **ماژول آماده دارید؟**
>
> اگر روی WHMCS هستید، لازم نیست چیزی کد بزنید — افزونه ادپیکس همین API را صدا می‌زند. آن را از «مدیریت» ← «یکپارچه سازی ها و ماژول‌های CRM» بگیرید و همان جا کلید سرور را بسازید.

## کلید سرور بسازید

کلید سرور به یک **دارایی** بسته می‌شود و از پنل هر جریان داده ساخته می‌شود.

1. در کنسول «مدیریت» را باز کنید.
2. در ستون «تنظیمات دارایی»، «جریان‌های داده» را انتخاب کنید (یا «یکپارچه سازی ها و ماژول‌های CRM» — کارت کلیدها در هر دو هست).
3. در کارت «کلیدهای سرور»، جریان را انتخاب کنید، یک نام بدهید و دامنه‌های لازم را تیک بزنید.
4. «ساخت کلید سرور» را بزنید و مقدار `sk_…` را همان لحظه در جای امن بک‌اند بگذارید.

> **رمز فقط یک بار نشان داده می‌شود**
>
> از کلید فقط پیشوند یازده کاراکتری اش نگه داشته می‌شود؛ خود رمز به صورت هش ذخیره می‌شود و بازیابی نمی‌شود. اگر گمش کردید، کلید تازه بسازید و قدیمی را ابطال کنید. ساخت و ابطال کلید هر دو در گزارش ممیزی ثبت می‌شوند.

چهار دامنه دسترسی وجود دارد و کمترین مجموعه لازم را بدهید:

| دامنه | چه چیزی را باز می‌کند |
| --- | --- |
| `events` | ارسال رویداد و سفارش (`/events` و `/events/batch`) |
| `identify` | وصل کردن یک بازدیدکننده ناشناس به کاربر شناخته شده (`/identify`) |
| `read:attribution` | خواندن اتریبیوشن زنده یک کاربر یا عکس فوری یک سفارش |
| `read:identity` | خواندن گراف هویت یک کاربر (شناسه‌های مرتبط، مشخصات انباشته) |

ساختن کلید در سطح «ویرایش گر» به بالا ممکن است؛ دیدن فهرست کلیدها با دسترسی خواندن گزارش هم کافی است — ولی فهرست فقط پیشوند را نشان می‌دهد، نه رمز را.

> **کلید نوشتن، کلید سرور نیست**
>
> هر جریان داده یک «کلید نوشتن» هم دارد که برای مسیر مرورگر و دروازه تگ است. API سرور به سرور آن را نمی‌پذیرد. فقط کلیدی که با `sk_` شروع می‌شود اینجا کار می‌کند.

## اولین رویداد را بفرستید

پایه آدرس `https://api.adpix.io/api/v1/s2s` است. هر درخواست دو چیز را با خود می‌آورد: کلید در هدر `Authorization` و شناسه دارایی در هدر `X-Sov-Site`.

```bash
curl -X POST https://api.adpix.io/api/v1/s2s/events \
  -H 'Authorization: Bearer sk_...' \
  -H 'X-Sov-Site: <property_id>' \
  -H 'Content-Type: application/json' \
  -d '{
    "event": "purchase",
    "event_id": "order_10482",
    "order_id": "10482",
    "email": "ali@example.com",
    "value": 4900000,
    "currency": "IRR",
    "properties": { "gateway": "saman" }
  }'
```

```json
{
  "accepted": true,
  "event_id": "order_10482",
  "global_user_id": "...",
  "anonymous_linked": true,
  "attribution_snapshot": { "channel": "Paid Search", "attribution_model": "last_non_direct" },
  "request_id": "req_..."
}
```

پیش از سیم کشی کامل، یک بار `POST /api/v1/s2s/verify` را صدا بزنید. اگر کلید و دارایی با هم بخوانند، پاسخ نام و منطقه زمانی دارایی، دامنه‌های کلید، طول پنجره اتریبیوشن و اجباری بودن یا نبودن HMAC را برمی‌گرداند — دقیقا همان چیزی که برای عیب یابی تنظیمات لازم دارید.

| هدر | الزامی | توضیح |
| --- | --- | --- |
| `Authorization: Bearer sk_…` | بله | کلید سرور. نبودنش یا شکل نادرستش پاسخ ۴۰۱ می‌گیرد. |
| `X-Sov-Site` | بله | شناسه دارایی ای که کلید برای آن ساخته شده است. |
| `Content-Type: application/json` | بله | بدنه تا حداکثر یک مگابایت خوانده می‌شود. |
| `X-Sov-Timestamp` | فقط با HMAC | زمان یونیکس به ثانیه. |
| `X-Sov-Signature` | فقط با HMAC | امضا به شکل `t=<ts>,v1=<hex>`. |

از میدان های بدنه، فقط `event` و `event_id` الزامی‌اند. `value` و `currency` برای درآمد است (کد ارز باید سه حرف بزرگ باشد)، `items` سطرهای سفارش را می‌سازد، و هر چیز دیگری را در `properties` بگذارید تا در گزارش‌ها به عنوان پارامتر رویداد در دسترس باشد.

## `event_id` — ضامن اینکه یک سفارش دو بار شمرده نشود

این مهم ترین قاعده این صفحه است. `event_id` را شما تعیین می‌کنید و باید در سیستم شما به طور پایدار همان رویداد را نشان دهد — مثلا `order_10482`، نه یک عدد تصادفی تازه در هر تلاش.

ادپیکس پاسخ اولین ارسال هر `event_id` را به همراه اثر انگشت بدنه درخواست نگه می‌دارد. بعد از آن:

| ارسال دوباره با… | نتیجه |
| --- | --- |
| همان `event_id` و همان بدنه | همان پاسخ قبلی با کد ۲۰۰ برمی گردد؛ رویداد تازه ای نوشته نمی‌شود |
| همان `event_id` و بدنه متفاوت | کد ۴۰۹ و خطای `idempotency_conflict`؛ هیچ چیز نوشته نمی‌شود |
| `event_id` تازه | یک رویداد جدید |

پس تلاش دوباره شبکه، اجرای دوباره صف کارها و ری استارت وسط کار همگی بی خطرند. این تنها مسیر جمع آوری در ادپیکس است که چنین تضمینی می‌دهد؛ مرورگر چنین تضمینی ندارد.

## بازدیدکننده را به کاربر واقعی وصل کنید

هر رویداد باید بگوید موضوعش کیست. چهار راه دارید و می‌توانید بیش از یکی را با هم بفرستید:

- `anonymous_id` — همان شناسه ناشناسی که مرورگر ساخته است. اگر این را بفرستید، رفتار پیش از ورود کاربر هم به همین شخص می‌چسبد.
- `external_id` — شناسه کاربر در سیستم خودتان (شناسه مشتری CRM، شناسه کاربر پنل).
- `email` — قطعی.
- `phone` — قطعی و هم رتبه ایمیل. شماره با کد کشور پیش‌فرض دارایی به شکل بین المللی نرمال می‌شود، پس شماره ملی ۰۹۱۲… و شکل بین المللی همان شماره به یک کاربر می‌رسند.

ایمیل، شماره و شناسه بیرونی لینک های **قطعی** اند و هیچ وقت با حدس اثر انگشت مرورگر بازنویسی نمی‌شوند. برای لحظه ورود یا ثبت نام — جایی که هنوز رویداد کسب‌وکاری ندارید — `POST /identify` را صدا بزنید؛ همان لینک را برقرار می‌کند و شناسه کاربر سراسری، شناسه‌های ناشناس مرتبط و اتریبیوشن فعلی او را برمی‌گرداند. منطق کامل در [ادپیکس چطور بازدیدکننده ها را می‌شناسد](concepts/foundations/how-adpix-identifies-visitors) آمده است.

## سفارش ها و عکس فوری اتریبیوشن

اگر `order_id` بفرستید (یا رویداد را `purchase`، `order_updated`، `order_refunded` بنامید و شناسه سفارش را همراه کنید)، ادپیکس یک **عکس فوری اتریبیوشن** برای آن سفارش می‌سازد و منجمد می‌کند: کانال، منبع، کمپین، اولین و آخرین برخورد، فاصله روزها تا تبدیل و تعداد برخوردها.

سرمشق این عکس فوری «آخرین برخورد غیرمستقیم» است: اگر آخرین برخورد مستقیم یا خالی باشد، اولین برخورد جای آن را می‌گیرد.

عکس فوری **یک بار** نوشته می‌شود. به روزرسانی بعدی همان سفارش، مقدار و وضعیت را می‌آورد ولی اتریبیوشن دست نخورده می‌ماند — یعنی گزارش شما با تغییر رفتار بعدی مشتری بازنویسی نمی‌شود. برای خواندنش:

```bash
curl 'https://api.adpix.io/api/v1/s2s/attribution?order_id=10482' \
  -H 'Authorization: Bearer sk_...' \
  -H 'X-Sov-Site: <property_id>'
```

## ارسال دسته ای

برای پرکردن گذشته یا صف شبانه، `POST /events/batch` با بدنه `{"events":[…]}` بفرستید. سقف هر درخواست ۵۰۰ رویداد است؛ بیشتر از آن پاسخ ۴۱۳ می‌گیرد.

پاسخ یک آرایه `results` است، به همان ترتیب ورودی و یک نتیجه به ازای هر رویداد. یک رویداد نامعتبر بقیه را زمین نمی‌زند — نتیجه خودش خطا می‌شود و بقیه پذیرفته می‌شوند. بنابراین همیشه `results` را بخوانید؛ کد وضعیت کلی درخواست کافی نیست.

## امضای HMAC

اگر هنگام ساخت کلید «نیازمند HMAC» را تیک زده باشید، هر درخواست باید امضا هم داشته باشد. رشته امضا `<timestamp>.<بدنه خام>` است و کلید امضا همان رمز `sk_…`:

```
X-Sov-Timestamp: 1767225600
X-Sov-Signature: t=1767225600,v1=<hex(HMAC-SHA256)>
```

اختلاف زمان بیش از ۳۰۰ ثانیه با ساعت سرور، پاسخ `stale_timestamp` می‌گیرد — پس ساعت سرورتان باید همگام باشد. امضا روی **بایت های خام بدنه** حساب می‌شود، نه روی JSON بازتولیدشده؛ اگر بدنه را دوباره سریال کنید امضا نمی‌خواند.

اگر هدر امضا را بفرستید، حتی وقتی کلید HMAC را اجباری نکرده باشد، بررسی می‌شود.

## این رویدادها در گزارش‌ها چه شکلی‌اند

چند تفاوت عمدی با رویدادهای مرورگر هست که اگر ندانید، خیال می‌کنید داده گم شده است.

- **نشست ندارند.** یک رویداد سمت سرور نشست مرورگری نیست و ادپیکس برایش نشست نمی‌سازد. اگر می‌ساخت، هر سفارش CRM یک «نشست» تازه می‌شد و نرخ تبدیل را کوچک نشان می‌داد. نتیجه: این رویدادها در سنجه های نشست محور (نرخ تعامل، نشست به ازای کاربر) شمرده نمی‌شوند، ولی در شمارش رویداد، رویداد کلیدی، درآمد و اتریبیوشن کاربر کاملا حاضرند.
- **آدرس صفحه ندارند.** گزارش‌های صفحه محور آن‌ها را نشان نمی‌دهند.
- **اتریبیوشنشان از تاریخچه همان کاربر می‌آید.** اگر در `context` مقدار `first_touch` یا `last_touch` بفرستید همان به کار می‌رود؛ وگرنه ادپیکس سراغ اولین برخورد ماندگاری می‌رود که از مسیر مرورگر برای آن بازدیدکننده ذخیره کرده است. برای همین است که فرستادن `anonymous_id` تفاوت ایجاد می‌کند: بدون آن، سفارش به کمپینی که واقعا آن را آورده وصل نمی‌شود.
- **موقعیت جغرافیایی و دستگاه از `context` می‌آید.** `context.ip` و `context.user_agent` را بفرستید تا کشور و نوع دستگاه پر شود؛ اگر نفرستید خالی می‌ماند، چون IP سرور شما جای IP کاربر را نمی‌گیرد.

> **قواعد رویداد روی این مسیر اجرا نمی‌شوند**
>
> قواعد «ساختن رویداد» و «اصلاح رویداد» هنگام جمع آوری روی رویدادهای مرورگر اعمال می‌شوند و مسیر سرور به سرور از آن‌ها عبور نمی‌کند. رویداد را با همان نام و همان پارامترهایی بفرستید که می‌خواهید در گزارش ببینید. اگر برای صفحه موفقیت پرداخت یک قاعده «ساختن رویداد» دارید و **همزمان** همان خرید را از سرور هم می‌فرستید، دو تبدیل مستقل ثبت می‌شود؛ یکی از دو مسیر را انتخاب کنید. شرح کامل در [قواعد ساختن و اصلاح رویداد](analytics/collect/event-rules).

## خطاها

همه خطاها JSON برمی گردانند و همیشه یک `request_id` دارند. اگر تیکت می‌زنید، همان را بفرستید.

| کد | خطا | معنی |
| --- | --- | --- |
| ۴۰۱ | `invalid_key` | هدر `Authorization` یا `X-Sov-Site` نیست، یا کلید ناشناخته/ابطال شده است |
| ۴۰۱ | `bad_signature` / `stale_timestamp` | امضای HMAC نمی‌خواند یا اختلاف زمان از ۳۰۰ ثانیه بیشتر است |
| ۴۰۳ | `property_mismatch` | کلید برای این دارایی ساخته نشده است |
| ۴۰۳ | `insufficient_scope` | کلید دامنه لازم این مسیر را ندارد |
| ۴۰۰ | `bad_request` | بدنه JSON معتبر نیست |
| ۴۲۲ | `validation_error` | `event` یا `event_id` نیست، یا کد ارز سه حرفی بزرگ نیست |
| ۴۰۹ | `idempotency_conflict` | همان `event_id` قبلا با بدنه دیگری ثبت شده است |
| ۴۱۳ | `too_many_events` | دسته بیش از ۵۰۰ رویداد دارد |
| ۴۲۹ | `rate_limited` | سقف نرخ همان کلید پر شده؛ هدر `Retry-After` را ببینید |

سقف نرخ روی خود کلید حساب می‌شود. اگر یک کار سنگین پرکردن گذشته دارید، برایش کلید جداگانه بسازید تا ترافیک زنده فروشگاه را نبندد.

## اندپوینت قدیمی `/api/v1/track`

یک مسیر قدیمی تر هم هست که همان پوشش رویداد تگ مرورگر را با کلید سرور می‌پذیرد. هدر دارایی نمی‌گیرد، عکس فوری اتریبیوشن نمی‌سازد و هیچ تضمینی درباره ارسال دوباره نمی‌دهد. فقط برای یکپارچه سازی های قدیمی نگه داشته شده است؛ کار تازه را روی `/api/v1/s2s/events` بنویسید.

شکل کامل درخواست‌ها و پاسخ‌ها، برای وقتی که خودتان کلاینت می‌نویسید، در [مرجع API سرور به سرور](analytics/developers/s2s-api) است.

## پرسش‌های پرتکرار

### چه وقت باید سراغ ارسال سمت سرور بروم؟

وقتی رویداد در مرورگر رخ نمی‌دهد یا در مرورگر قابل اعتماد نیست — تایید پرداخت که درگاه به سرور شما اعلام می‌کند، تمدید و صورت حساب دوره‌ای، لغو و بازگشت وجه، و سفارشی که اپراتور از پنل ثبت می‌کند. برای بازدید صفحه و رفتار کاربر در سایت، همچنان تگ مرورگر درست ترین منبع است.

### اگر همان درخواست را دوباره بفرستم، سفارش دو بار شمرده می‌شود؟

خیر، به شرطی که `event_id` یکسان باشد. ادپیکس پاسخ اولین ارسال را نگه می‌دارد و برای ارسال دوم همان پاسخ را با کد ۲۰۰ برمی‌گرداند، بدون آنکه رویداد تازه ای بسازد. اگر همان `event_id` را با بدنه متفاوتی بفرستید، پاسخ ۴۰۹ است و چیزی نوشته نمی‌شود.

### کلید سرور با کلید نوشتن جریان داده فرق دارد؟

بله و جای هم را نمی‌گیرند. کلید نوشتن برای مسیر مرورگر و دروازه تگ است؛ API سرور به سرور فقط کلید سرور (`sk_`) را می‌پذیرد. اگر کلید نوشتن را در هدر Authorization بگذارید پاسخ ۴۰۱ می‌گیرید.

### چرا رویدادهای سمت سرور در گزارش‌های نشستی دیده نمی‌شوند؟

چون یک رویداد سمت سرور، نشست مرورگری ندارد و ادپیکس عمدا برایش نشست جعلی نمی‌سازد. اگر می‌ساخت، هر سفارش CRM یک نشست تازه می‌شد و نرخ تبدیل را خراب می‌کرد. این رویدادها در شمارش رویداد، رویداد کلیدی، درآمد و اتریبیوشن کاربر کاملا حاضرند؛ فقط در سنجه هایی که واحدشان نشست است شمرده نمی‌شوند.

## مطالب مرتبط

- [مرجع API سرور به سرور](https://docs.adpix.io/fa/developers/analytics/developers/s2s-api/)
- [رویدادهای تجارت الکترونیک](https://docs.adpix.io/fa/analytics/collect/ecommerce-events/)
- [یکپارچه‌سازی‌ها و ماژول‌های CRM](https://docs.adpix.io/fa/analytics/admin/integrations/)
- [ادپیکس بازدیدکننده را چطور می‌شناسد](https://docs.adpix.io/fa/concepts/foundations/how-adpix-identifies-visitors/)

---

[مستندات](https://docs.adpix.io/fa/analytics/collect/server-side-tracking/) · AdPix
