İçeriğe geç
Belgeler menüsüTracking API kılavuzu

Postlip Metrics Tracking API — v1

Ürünlerin Postlip Metrics'e olay göndermesi için sözleşme. Segment Tracking API Spec ile uyumludur: aynı uçlar, aynı mesaj alanları, aynı kimlik doğrulama. Segment ya da RudderStack istemci kütüphaneleri host/apiHost ayarı Postlip Metrics'e çevrilerek kullanılabilir; Postlip Metrics'in kendi SDK'ları (@metrics/web, @metrics/node) ek olarak imza, ret sebebi loglama ve şemadan tip üretimi sunar.

Bu sayfa derleme sırasında depodaki docs/api/tracking-api.md dosyasından üretilir.

  • Taban adres: https://collect.<alan>/v1 (geliştirme: http://localhost:7110/v1)
  • Makine tarafından okunur belge: openapi/tracking.json (koddan üretilir)

1. Kavramlar

KavramAçıklama
UygulamaÖlçülen ürün (ör. HukukPlus). Olay kataloğu uygulamaya aittir.
KaynakUygulamaya veri gönderen istemci: web sitesi, iOS, Android, sunucu. Her kaynağın kendi anahtarları vardır.
Yazma anahtarıpk_live_… / pk_test_… — tarayıcı ve mobilde açıkta durabilir; yalnız alım yapar.
İmza anahtarısk_live_… / sk_test_… — yalnız sunucuda; isteği imzalar.
Test anahtarı_test_ anahtarla gelen her olay is_test = true işaretlenir; ekranlar varsayılan olarak dışlar.
Olay kataloğuUygulamanın panelden tanımladığı olaylar ve özellikleri. Doğrulama kipi: strict (tanımsız → ret), lenient (kabul, "keşfedilen"), discover (kabul + taslak tanım).

2. Kimlik doğrulama

Üç yoldan biri (öncelik sırasıyla):

HTTP
Authorization: Basic base64("<writeKey>:")         # Segment uyumlu
Authorization: Bearer <writeKey>
{ "writeKey": "<writeKey>", ... }                    # yalnız tarayıcı beacon'ı (text/plain gövde)

İmza (sunucu kaynakları)

Kaynak ayarında "İmza zorunlu" açıksa imzasız istek 401 signature_required alır.

HTTP
Metrics-Signature: t=1793892559,v1=5f0c…e1a2

v1 = hex(HMAC-SHA256(signingSecret, t + "." + hamGövde)). t Unix saniyedir; sunucu saatinden ±300 sn dışındaysa 401 signature_expired. Birden çok v1= değeri (anahtar yenileme dönemi) kabul edilir.

3. Uçlar

UçMesaj tipi
POST /v1/trackKullanıcının yaptığı bir eylem
POST /v1/identifyKişiyi kullanıcı kimliğiyle tanıtma, kişi özellikleri
POST /v1/pageWeb sayfası görüntüleme
POST /v1/screenMobil ekran görüntüleme
POST /v1/groupKişiyi bir hesaba (şirket/büro/site) bağlama, hesap özellikleri
POST /v1/aliasİki kimliği birleştirme (previousId → userId)
POST /v1/batchKarışık tiplerde en fazla 500 mesaj
POST /v1/pingBağlantı testi: anahtarı ve (varsa) imzayı doğrular, hiçbir şey kaydetmez
GET /v1/config/{writeKey}SDK uzak yapılandırması (?schemaHints=1 ile olay planının özeti, aşağıda)
GET /v1/healthSağlık

POST /v1/ping gövdesi {} (imzalanır), yanıtı {"success": true, "sourceId", "appId", "environment", "test", "signature": "valid" | "absent"}; anahtar ya da imza hatasında §6'daki 401/403.

Gövde application/json; tarayıcıdan text/plain;charset=UTF-8 da kabul edilir (CORS ön isteği olmasın diye).

GET /v1/config/{writeKey}?schemaHints=1 (2026-11-08'de eklendi; geriye uyumlu — parametresiz yanıt değişmedi) yanıta schemaHints ekler: { "mode": "strict" | "lenient" | "discover", "events": [{ "name", "required": [özellik anahtarları] }], "blocked": [olay adları], "truncated": bool } — engellenmemiş tanımlar ve zorunlu özellikleri, engellenen olaylar (en çok 300'er), yalnız adlar (tip, açıklama, kişisel veri sınıfı yok). Web SDK'nın npm paketi debug kipinde ister ve tanımsız, engellenen olay ya da eksik zorunlu özellik için konsola uyarı yazar; gönderim bundan etkilenmez.

4. Mesaj alanları

Ortak alanlar (Segment Spec):

AlanTipZorunluAçıklama
typestringbatch içindetrack, identify, page, screen, group, alias
messageIdstring ≤ 100önerilirİdempotentlik anahtarı. Aynı messageId + timestamp ikinci kez yazılmaz. Gerçek olaydan belirleyici üretin (ör. purchase:<sipariş no>). Yoksa sunucu içerik özetinden üretir.
anonymousIdstring ≤ 200userId yoksaCihaz/tarayıcı kimliği
userIdstring ≤ 200anonymousId yoksaUygulamanın kendi kullanıcı kimliği. Opak olmalı; e-posta/telefon/TCKN kullanmayın.
timestampISO 8601hayırOlay anı; yoksa alım anı. 31 günden eski ya da 1 saatten ileri tarih reddedilir.
sentAtISO 8601hayırİstemcinin gönderdiği an; saat kayması düzeltmesinde kullanılır.
contextobjecthayır§5
integrationsobjecthayırHedef bazında aç/kapa: { "meta_capi": false }

Tipe özel alanlar:

TipAlanlar
trackevent (string ≤ 200, zorunlu), properties (object)
identifytraits (object)
pagename, category, properties (url, path, title, referrer, search)
screenname, category, properties
groupgroupId (zorunlu), traits
aliaspreviousId (zorunlu), userId (zorunlu)

Standart properties (bütün olaylarda anlamlı): revenue, value, currency (ISO 4217; listede olmayan kod currency_unknown uyarısı alır), tax, net_revenue, order_id / transaction_id, plan_id, test (boolean — anahtar canlı olsa da test işaretler). Bunlar olay kataloğunda tanımlanmasa da her doğrulama kipinde (strict dahil) kabul edilir, tipleri her zaman denetlenir; olayda aynı anahtarla tanım varsa tanım geçerlidir. Liste: @metrics/contracts → STANDARD_PROPERTIES.

5. context

Segment Spec'teki adlarla; Postlip Metrics'in kullandıkları:

AlanAçıklama
ipSunucu kaynağı son kullanıcının IP'sini iletebilir (konum ve iç trafik için). Saklanmaz. Yoksa isteğin IP'si (yalnız tarayıcı/mobil kaynak).
userAgent
locale, timezone
pageurl, path, title, referrer, search
campaignsource, medium, name, content, term, id — yoksa sayfa URL'sinden çıkarılır
referrer{ "type": "...", "id": "..." }
clickIdsPostlip Metrics eki: gclid, gclid_at, gbraid, wbraid, dclid, fbclid, fbc, fbp, li_fat_id, msclkid, ttclid, yclid
attributionPostlip Metrics eki: first_touch, last_non_direct_touch, session_touch — her biri {source, medium, campaign, content, term, campaign_id, landing_path, referrer_host, at}. Sunucu olayına tarayıcının bildirdiği kaynağı iletmek için; web SDK'da metrics.getContext() verir. Ayrıca method (2026-11-08'de eklendi): consent — temaslar ziyaretler arasında saklandı (web SDK'da pazarlama rızasıyla), same_visit — yalnız bu ziyaret (rızasız); talebin atıf görüntüsünde görünür. Başka değer atılır, ret yok.
consentPostlip Metrics eki: { "analytics": bool, "marketing": bool }. Pazarlama rızası yoksa tıklama kimlikleri saklanmaz ve hedeflere gönderilmez. Reklam platformlarına dönüşüm geri gönderiminde (Meta CAPI, Google Ads, LinkedIn CAPI, GA4) tıklama kimliği yalnız marketing: true açıkça gönderildiğinde kullanılır; alan yoksa rıza yok sayılır. Sunucu olayında tarayıcının bildirdiği rızayı bu alanla iletin (web SDK'da metrics.getContext() verir).
sessionPostlip Metrics eki: { "id": "...", "start": bool }
appname, version, build, namespace
devicetype, manufacturer, model, id, advertisingId (yalnız pazarlama rızasıyla)
osname, version
screenwidth, height, density
libraryname, version
traitsKişi özellikleri (identify çağırmadan)
groupIdOlayın ait olduğu hesap
testtrue → test trafiği (anahtarın test kipine ek). Web SDK'da ?metrics_debug=1 bağlantısı oturum boyunca açar.
internalPostlip Metrics eki (2026-11-08'de belgelendi): true → iç trafik. Web SDK'da ?metrics_internal=1 bağlantısıyla işaretlenen tarayıcı (=0 kaldırır). Geçersiz değer atılır, ret yok.
gaPostlip Metrics eki (2026-11-08'de eklendi): { "clientId": "<rakam>.<rakam>", "sessionId": "<rakam>" } — tarayıcının _ga / _ga_<id> çerezlerinden (web SDK yalnız analitik rızasıyla okur). GA4 Measurement Protocol hedefi Postlip Metrics'in kimlikleri yerine bunları gönderir, dönüşüm GA4 oturumuna katılır; olayda yoksa oturumunun son ilettiği. Geçersiz değer atılır, ret yok.

Rızasız ziyaretçi (2026-11-08): kaynağın rıza kipi required iken analitik rızası olmayan tarayıcı mesajında (consent.analytics true değil; sunucu kaynakları hariç) sekmeye özgü anonymousId yerine günün ziyaretçi kimliği saklanır: ck_ + HMAC(günün tuzu, IP + tarayıcı + uygulama). Tuz her UTC günü rastgele üretilir ve gün bitince silinir — ertesi gün aynı tarayıcı başka ziyaretçidir, eski kimlik yeniden hesaplanamaz; IP yalnız girdidir, saklanmaz. userId gönderilmişse kişi yine onunla tanınır.

Tıklanan iletişim bağlantıları (2026-11-08): $click / $rageclick hedefi tel:, sms:, mailto: ya da WhatsApp (wa.me, api.whatsapp.com, web.whatsapp.com) bağlantısıysa saklanan hedefe tür eklenir (target.contact: phone, email, whatsapp) ve bağlantı şemasına ya da alan adına kısaltılır; $outbound WhatsApp adresi de yalnız alan adıyla saklanır. Numara ve adres hiç saklanmaz. Sistem olaylarına $pageleave.scroll_reversals (tamsayı, isteğe bağlı: yarım ekrandan büyük yön değişimi sayısı; aşırı kaydırma sinyali) eklendi; eski SDK'lar göndermez.

6. Yanıtlar

JSON
200
{
  "success": true,
  "accepted": 2,
  "rejected": [
    {
      "index": 2,
      "messageId": "purchase:9f2c",
      "errors": [
        { "code": "property_required", "path": "properties.plan_id", "message": "plan_id zorunlu" }
      ]
    }
  ],
  "warnings": [
    { "index": 0, "code": "property_unplanned", "path": "properties.color" }
  ]
}
KodAnlamİstemci ne yapar
200Parti işlendi. Reddedilenler rejected[]'de; düzeltmeden tekrar göndermeyin. Tekrar eden messageId kabul sayılır.Reddedileni logla.
400Gövde hiç okunamadı ya da bütün mesajlar geçersizLogla, tekrar etme.
401Anahtar yok/yanlış, imza yok/yanlış/süresi geçmişAnahtarı ve sunucu saatini denetle; tekrar dene + alarm.
403Kaynak kapalı ya da Origin izinli alan adlarında değilLogla.
413Mesaj > 32 KB ya da parti > 500 KB / 500 mesajPartiyi ikiye böl.
429Hız sınırı (kaynak başına, mesaj sayısıyla; gövde {"success": false, "error": {"code": "rate_limited", …}}, hiçbir mesaj saklanmadı)Retry-After (saniye) kadar bekle, aynı partiyi tekrar gönder.
5xx / ağ hatasıGeçiciÜstel geri çekilme: 1 dk, 5 dk, 30 dk, 2 sa… en fazla 24 sa.

Hız sınırı kaynak başınadır ve mesajları sayar (500 mesajlık parti 500 sayılır): sürekli dakikada 60.000, bir anda 10.000 mesaj (kurulumun ayarı). Aşan istek hiçbir şey saklamadan 429 alır; Retry-After beklenip aynı messageId'lerle tekrar gönderilir. OpenAPI belgesinde (tracking.json) 429 yanıtının gövdesi (Refusal) ve Retry-After başlığı tanımlı, varsayılan sınırlar x-limits'te rateLimitPerMinute ve rateLimitBurst (2026-11-08).

Uygulamanın trafik filtresi kuralları (panelde Ayarlar; ör. test kampanyası adları, ofis IP aralığı, QA kullanıcı kimliği, test hesabı groupId) eşleşen mesajı test, iç ya da bot trafiği işaretler; mesaj yine kabul edilir (200), ekranlar varsayılan olarak dışlar. Her uygulamaya hazır kurallar eklenir (reklam etiket test araçlarından gelen ziyaret, test / qa_ ile başlayan ya da _test ile biten kampanya adı → test; Postlip Metrics panelinden gelen ziyaret → iç), uygulama bunları düzenleyebilir ya da silebilir. Saklanan alanlara bakan kural (IP ve tarayıcı kimliği hariç) uygulamanın geçmiş trafiğini de arka planda yeniden işaretler; işaret yalnız eklenir (2026-11-08).

Hata kodları (errors[].code): invalid_message, event_name_missing, identity_missing, event_unplanned (strict), event_blocked, property_required, property_type, property_unplanned (strict), property_too_long, pii_detected, timestamp_out_of_range, message_too_large.

Uyarı kodları (warnings[].code; mesaj kabul edilmiştir): yukarıdaki kodların reddetmeyen kipteki karşılıkları (ör. property_unplanned, pii_detected) ve currency_unknown — properties.currency ISO 4217 listesinde değil; tutar saklanır ama kur bulunamadığı için uygulamanın para birimine çevrilmeyebilir (2026-11-08'de eklendi; geriye uyumlu) — ve property_out_of_range — sayı tanımın minimum / maximum aralığının dışında; değer saklanır (aykırı tutar işaretlenir, kaybolmaz). Bu sürümden önce aralık dışı sayı strict kipte reddediliyor, diğer kiplerde atılıyordu; artık her kipte uyarıyla kabul edilir (2026-11-08; daha geniş kabul, istemci değişikliği gerekmez). Kabul edilen mesajların uyarıları örneklenerek (istek başına en çok 20, kaynak ve dakika başına en çok 600) 7 gün saklanır: panelin canlı olay akışında mesajın yanında ve kaynak sağlığında (warningsLast24h) görünür.

7. Örnekler

7.1 Sunucudan kayıt olayı (imzalı)

Bash
BODY='{"userId":"usr_7c1e","event":"signup_completed","messageId":"signup:7c1e","timestamp":"2026-11-06T14:46:00+03:00","properties":{"signup_method":"email","plan_id":"pro"},"context":{"consent":{"analytics":true,"marketing":true},"attribution":{"first_touch":{"source":"google","medium":"cpc","campaign":"hp_search_brand","landing_path":"/","at":"2026-11-02T10:11:00Z"}},"clickIds":{"gclid":"Cj0KCQiA…"}}}'
T=$(date +%s)
SIG=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$SIGNING_SECRET" -hex | sed 's/^.* //')
curl -X POST https://collect.<alan>/v1/track \
  -u "$WRITE_KEY:" -H "Content-Type: application/json" \
  -H "Metrics-Signature: t=$T,v1=$SIG" -d "$BODY"

7.2 Toplu gönderim

JSON
POST /v1/batch
{
  "batch": [
    { "type": "identify", "userId": "usr_7c1e", "anonymousId": "a1b2c3", "traits": { "plan": "pro" } },
    { "type": "group", "userId": "usr_7c1e", "groupId": "acc_903427", "traits": { "team_size": "6-20" } },
    { "type": "track", "userId": "usr_7c1e", "event": "purchase_completed", "messageId": "purchase:HP9f2c",
      "properties": { "transaction_id": "HP9f2c", "plan_id": "pro", "revenue": 5197.6, "net_revenue": 4331.33,
                      "tax": 866.27, "currency": "TRY", "billing_period": "yearly" } }
  ]
}

7.3 Tarayıcı (script etiketi)

HTML
<script>
  !function(){var m=window.metrics=window.metrics||[];if(m.load)return;m.q=[];["page","track","identify","group","alias","reset","consent","ready"].forEach(function(k){m[k]=function(){m.q.push([k].concat([].slice.call(arguments)))}});
  var s=document.createElement("script");s.async=1;s.src="https://collect.<alan>/m.js";document.head.appendChild(s);
  m.load=function(k,o){m.q.unshift(["load",k,o])};}();
  metrics.load("pk_live_…", { consent: "required" });
  metrics.page();
</script>

7.4 Segment istemcisiyle

JavaScript
import { Analytics } from '@segment/analytics-node';
const analytics = new Analytics({ writeKey: 'pk_live_…', host: 'https://collect.<alan>', path: '/v1/batch' });
analytics.track({ userId: 'usr_7c1e', event: 'trial_started', properties: { plan_id: 'pro', trial_days: 14 } });

(İmza zorunlu kaynaklarda Segment istemcisi imza atamaz; Postlip Metrics SDK'sını ya da imzasız sunucu kaynağı kullanın.)

8. Olay tasarımı önerileri

  • Olay adı geçmiş zamanlı ve nesne + eylem: signup_completed, trial_started, purchase_completed, demo_requested. Ad bir kez yayımlandıktan sonra değiştirilmez; yeniden adlandırma panelde takma adla yapılır.
  • Her iş olayını panelde bir iş rolüne bağlayın (signup, purchase, lead…). Dashboard, huniler, reklam ROAS'ı ve dönüşüm geri gönderimi role bakar.
  • Para: revenue vergi dahil brüt, net_revenue vergi hariç, tax vergi, currency zorunlu.
  • Kişisel veri göndermeyin. Gerekirse (ör. Meta CAPI eşleşmesi) özelliği panelde personal + hash olarak tanımlayın; Postlip Metrics özetler, ham değeri saklamaz.
  • Aynı gerçek olay için aynı messageId'yi üretin (ödeme sağlayıcının geri çağrısı iki kez gelse de tek olay).