Varify Realtime Tracking
17 Eylül 2026 veya sonrasında oluşturulan hesaplar, „Tracking Setup“ altında doğrudan „Varify Realtime Tracking“ seçeneğini seçebilir. Mevcut müşteriler bu özelliği iletişim formumuz üzerinden talep edebilir. GA4 ile birlikte entegrasyon, talep üzerine mevcut müşterilere sunulmaktadır.
Kısaca
Varify Realtime Tracking, deneylerini doğrudan Varify içinde gecikmeden analiz etmeni sağlar. Özel bir Varify izleme servisi, tanımladığın hedefleri ölçer ve Varify Reporting içinde gösterir.
Bunu iki şekilde kullanabilirsin:
- İzleme sağlayıcın olarak: Varify, deneylerini kendi başına ölçer ve raporlarının temelini oluşturur. Bunu „Tracking Setup“ altında seçersin.
- GA4 ile birlikte entegrasyon olarak: „Tracking Setup“ altında Google Analytics 4'ü kurmuşsundur ve ek olarak „Integrations“ altında Varify Realtime Tracking'i açarsın.
Sağlayıcı mı, entegrasyon mu?
| İzleme sağlayıcı olarak | GA4 ile birlikte entegrasyon olarak | |
|---|---|---|
| Nerede açarsın | „Tracking Setup,“ „Varify Realtime Tracking“ kartı | „Integrations,“ „Varify Realtime Tracking“ kartı |
| Gereksinim | Hesabın için kullanılabilir olması | GA4'ün „Tracking Setup“ içinde kurulmuş olması ve entegrasyonun hesabın için etkinleştirilmiş olması |
| Kullanıcı onayı | Kurulum sırasında sen seçersin | GA4 kurulumundan devralınır |
| Raporlarda gösterilen | Varify Realtime Tracking verileri | Varify hedefleri ve GA4 hedefleri (ayrıca BigQuery), isteğe bağlı olarak Shopify |
| GA4 hedefleri | İçe aktarılamaz | Her zamanki gibi içe aktarılır |
Varify Realtime Tracking'i izleme sağlayıcın olarak kullan
Adım 1: Tracking sistemini kur
„Tracking Setup“ bölümüne git ve „Varify Realtime Tracking“ seçeneğini seç. Ardından Varify Realtime Tracking'i „User Consent“ ile mi yoksa onsuz mu entegre edeceğine karar verebilirsin. Bu kararı vermene yardımcı olacak sayfa: https://varify.io/varify-tracking-gdpr-compliance/
Adım 2: Varify Realtime Tracking'i Etkinleştirme
Kullanıcı onayı olmadan entegrasyon
Varify Realtime Tracking'i kullanıcı onayı olmadan yüklemeye karar verdiysen, temel izleme kurulumun zaten yapılmıştır ve aşağıdaki „Set up your goals“ bölümüyle devam edebilirsin.
Kullanıcı onayıyla entegrasyon
Kullanıcı izniyle entegrasyon için bir aktivasyon betiği sağlanır. Bu betik yüklenir yüklenmez takip etkinleştirilir. Betiği, kendi onay yönetim platformunuzun (CMP) belirtimlerine göre entegre edin. Entegrasyon için sayfa görüntülemelerine ve geçerli bir onay sinyaline dayalı olarak Google Tag Manager (GTM) de kullanabilirsiniz.
Verileri GA4'e gönder
Deney verilerini GA4'e de göndermek için „Integrations“ altında Google Analytics 4 entegrasyonunu aç. GA4 hedefleri bu kurulumda içe aktarılamaz. Kurulumu burada bulabilirsin: Google Analytics 4 Direct Integration
Varify Realtime Tracking'i entegrasyon olarak kullan
GA4 izleme sağlayıcın ise ve Varify'ın deneylerini ek olarak ölçmesini istiyorsan bunu kullan. Raporlar daha sonra Varify hedeflerini ve GA4 hedeflerini birlikte, ayrıca BigQuery verilerini ve etkinleştirdiysen Shopify verilerini gösterir.
Adım 1: GA4'ü Tracking Setup içinde kur
Önce Google Analytics 4'ü kur. Seçenekleri burada bulabilirsin: Set up tracking
Adım 2: Entegrasyonu aç
- Ana navigasyonda „Integrations“ bölümünü aç.
- „Varify Realtime Tracking“ kartını bul ve toggle'ı aç.
Açamıyor musun? Entegrasyonun önce hesabın için etkinleştirilmesi gerekir. İletişim formumuz üzerinden bize mesaj gönder. Sana geri döneceğiz.
Adım 3: Kullanıcı onayı
Varify Realtime Tracking, GA4 kurulumunu takip eder. GA4 yalnızca kullanıcı onayından sonra izleme yapıyorsa, Varify Realtime Tracking de yalnızca onay verildikten sonra başlar. Ayrıntılar için bkz. Verileri yalnızca kullanıcı onayından sonra web analiz aracına gönder.
Hedeflerini belirle
Deneylerini analiz etmek için hedefler oluştur. "Goals" bölümünü sol taraftaki kenar çubuğunda bulacaksın. Hedefler her iki kurulumda da aynı şekilde oluşturulur.
"Goals" bölümünde üç tür hedef arasından seçim yapabilirsin: Click Goal, Page View Goal ve Custom Goal.
Click Hedeflerini Ayarlama
Click Goals ile, deney katılımcılarının öğelere ne sıklıkta tıkladığını ölçebilirsiniz. Bunun için öğeye özel bir seçici tanımlayın. Bir öğe için özel bir seçici belirlemek üzere, örneğin Varify'ın görsel düzenleyicisini kullanabilirsiniz. Düzenleyiciyi açın ve takip etmek istediğiniz öğeyi seçin. Ardından seçiciyi kopyalayın ve "Click Goal" altındaki "Selector" alanına girin.
Page View Hedeflerini Ayarlama
Sayfa Görüntüleme Hedefleri ile deney katılımcılarının belirli sayfaları veya sayfa türlerini ne sıklıkla görüntülediğini ölçebilirsin. Bunu yapmak için kriterlerine uyan sayfa görüntülemelerini izlemek üzere kendi özel kurallarını tanımlayabilirsin.
Custom Hedeflerini Ayarlama
Özel hedefler oluşturmak için Custom Goals kullanabilirsin. Örneğin, bir siparişin gelir değerini ölçmek istediğinde bu kullanılabilir. Custom Goals deneyinde JavaScript aracılığıyla tetiklenir.
Dönüşüm Sayımı
Bu ayar ile bir hedefin bir kez mi yoksa birden çok kez mi izleneceğini belirleyebilirsin. Varsayılan olarak, A/B testinde her hedef yalnızca bir kez izlenir, çünkü genellikle yalnızca bir kullanıcının hedefe ulaşıp ulaşmadığıyla ilgilenirsin. Ancak birden çok izleme, gelir hedefleri için faydalı olabilir: Bu durumda, bir kullanıcının sipariş değeri birden çok oturum boyunca birikebilir.
Shopify Checkout Hedeflerini İzleme
„Purchase“ veya „Start Checkout“ gibi Shopify etkinlik hedefleri, web siteniz üzerinden doğrudan takip edilemez. 2025 yılından itibaren Shopify, bunların yalnızca web sitenizdeki özel pixel aracılığıyla takip edilebilmesini zorunlu kılmıştır. Bu hedefleri takip etmek için „Settings > Customer Events“ bölümüne gidin ve „Add Custom Pixel“ düğmesine tıklayın. Pixel adını „Varify Realtime Tracking“ olarak ayarlayın ve „Add Pixel“e tıklayın.
Pikseli oluşturduktan sonra, önce „Customer Privacy“ bölümünü aç ve "Data Sale" ayarını "Data collected does not qualify as a data sale." olarak ayarla. Varify veri satmaz ve deney analizleriniz için yalnızca toplu değerleri izler.
„Permission“ için, kullanıcı onayı olmadan takip yapmak istiyorsanız—daha önce takip kurulumunda belirtildiği gibi—„Not required“ seçeneğini seçmelisiniz. Öte yandan, kullanıcı onayıyla takip yapıyorsanız, ayarı "Required" olarak bırakabilirsiniz.
Ardından, bu paragrafın altında vurgulanan kodu kopyalayın ve "Code" altına yapıştırın.
/**
* Varify Shopify Web Pixel — links experiments/variations a customer saw to
* whether they completed a purchase.
*/
let storage;
// Store's currency, used to normalize order totals before sending them.
const SHOP_CURRENCY = init.data?.shop?.paymentSettings?.currencyCode || 'EUR';
// Free FX API, used only when an order's currency differs from the store's.
const FX_API = 'https://api.frankfurter.dev/v1/latest';
const FX_TTL_MS = 6 * 60 * 60 * 1000;
const FX_TIMEOUT_MS = 2000;
const VARIFY_INGESTION_ENDPOINT = 'https://w.varify.io/events';
// One id per pixel session, reused for every event this pixel sends.
const pixelSessionId = crypto.randomUUID();
// Reads this pixel's saved data (experiments seen, anonymous id, etc.),
// cached in-memory after the first read.
async function getVarifyData() {
if (storage) {
return storage;
}
storage = (async () => {
try {
const raw = await browser.localStorage.getItem('varify-data');
return raw ? JSON.parse(raw) : {};
} catch (error) {
// Don't cache a broken read — retry from scratch next time.
storage = undefined;
console.error('Varify: error reading local storage', error);
return {};
}
})();
return storage;
}
// Units of `currency` per 1 unit of the store's currency, or null if
// unavailable — callers should skip sending rather than guess a rate.
async function getFxRate(currency) {
const cacheKey = `varify-fx-${SHOP_CURRENCY}-${currency}`;
try {
const cached = await browser.localStorage.getItem(cacheKey);
if (cached) {
const { rate, ts } = JSON.parse(cached);
if (rate && ts && Date.now() - ts < FX_TTL_MS) {
return rate;
}
}
} catch (error) {
// Fall through and fetch fresh below.
}
try {
const url = `${FX_API}?from=${encodeURIComponent(SHOP_CURRENCY)}&to=${encodeURIComponent(currency)}`;
let res;
if (typeof AbortController === 'function') {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), FX_TIMEOUT_MS);
try {
res = await fetch(url, { signal: controller.signal });
} finally {
clearTimeout(timer);
}
} else {
res = await fetch(url);
}
if (!res.ok) {
console.warn(`Varify: FX API returned ${res.status} for ${currency}.`);
return null;
}
const json = await res.json();
const rate = json?.rates?.[currency];
if (typeof rate !== 'number' || !isFinite(rate) || rate <= 0) {
console.warn(`Varify: FX API gave no usable rate for ${currency}.`, json);
return null;
}
await browser.localStorage.setItem(
cacheKey,
JSON.stringify({ rate, ts: Date.now() }),
);
return rate;
} catch (error) {
console.warn(`Varify: FX lookup failed for ${currency}.`, error);
return null;
}
}
// Varify's endpoint only accepts flat text/number values, so a list of
// experiments becomes two comma-separated strings (ids and variation ids).
function flattenExperiments(entries) {
return {
experiment_ids: entries.map((entry) => entry.experimentId).join(','),
variation_ids: entries
.map((entry) =>
entry.variationId === null ? 'original' : entry.variationId,
)
.join(','),
};
}
// Edge's identifier also contains "Chrome/", so it must be checked first.
function detectBrowser(userAgent) {
if (userAgent.includes('Firefox/')) return 'Firefox';
if (userAgent.includes('Edg/')) return 'Edge';
if (userAgent.includes('Chrome/')) return 'Chrome';
if (userAgent.includes('Safari/')) return 'Safari';
return 'Unknown';
}
function detectDevice(userAgent) {
if (/Tablet|iPad/i.test(userAgent)) return 'tablet';
if (/Mobi|Android/i.test(userAgent)) return 'mobile';
return 'desktop';
}
function detectOs(userAgent) {
if (/iPhone|iPad|iPod/i.test(userAgent)) return 'iOS';
if (userAgent.includes('Android')) return 'Android';
if (userAgent.includes('Win')) return 'Windows';
if (userAgent.includes('Mac')) return 'macOS';
if (userAgent.includes('Linux')) return 'Linux';
return 'Unknown';
}
// Browser timezone, e.g. "Europe/Berlin" — not GPS or IP-based location.
function detectLocation() {
try {
return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;
} catch {
return undefined;
}
}
// Non-identifying browser/page details, added to every event. The pixel
// sandbox may not expose all of these — each lookup is best-effort and
// simply omitted if unavailable.
function collectBrowserInfo() {
const info = {};
try {
const userAgent =
typeof navigator !== 'undefined' ? navigator.userAgent : undefined;
if (typeof userAgent === 'string') {
info.userAgent = userAgent;
info.browser = detectBrowser(userAgent);
info.device = detectDevice(userAgent);
info.os = detectOs(userAgent);
}
} catch (error) {
// Not available in this environment.
}
try {
if (typeof window !== 'undefined' && window.location) {
info.path = window.location.pathname;
info.domain = window.location.hostname;
}
} catch (error) {
// Not available in this environment.
}
const location = detectLocation();
if (location) {
info.location = location;
}
return info;
}
// Sends one event to Varify. Never throws — failures are logged and
// swallowed so the pixel/checkout is never affected. Falls back to reading
// the anonymous id from local storage if one wasn't passed in, and returns
// whichever id was actually used so the caller can persist it.
async function sendVarifyEvent(name, teamSqid, anonymousId, properties) {
const anonymousIdWithFallback =
anonymousId || localStorage.getItem('varify_anonymous_id');
if (!teamSqid || !anonymousIdWithFallback) {
console.warn(
`Varify: skipping "${name}" event — missing teamSqid or anonymousId.`,
'Team Sqid',
teamSqid,
'Anonymous ID',
anonymousIdWithFallback,
);
return anonymousIdWithFallback;
}
try {
await fetch(VARIFY_INGESTION_ENDPOINT, {
method: 'POST',
keepalive: true,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
events: [
{
id: crypto.randomUUID(),
name,
teamId: String(teamSqid),
anonymousId: anonymousIdWithFallback,
sessionId: pixelSessionId,
properties: { ...collectBrowserInfo(), ...properties },
},
],
}),
});
} catch (error) {
console.error(`Varify: error sending "${name}" event`, error);
}
return anonymousIdWithFallback;
}
// Fired by Varify's tracking script when a customer is shown an experiment.
// Remembers it locally so it can be attached to a later checkout event.
analytics.subscribe('varify', async (event) => {
const {
varify_experimentId,
varify_variationId,
varify_anonymousId,
teamId,
teamSqid,
storageType = 'localStorage',
} = event.customData || {};
if (!varify_experimentId || !teamId || !teamSqid) {
console.warn('Varify: event missing experimentId or teamId. Event:', event);
return;
}
const validVariationId =
varify_variationId !== undefined ? varify_variationId : null;
const existingData = await getVarifyData();
if (!existingData.data) {
existingData.data = [];
}
const experimentEntry = {
experimentId: varify_experimentId,
variationId: validVariationId,
timestamp: Date.now(),
};
existingData.data = existingData.data.filter(
(entry) => entry.experimentId !== varify_experimentId,
);
existingData.data.push(experimentEntry);
existingData.teamId = teamId;
existingData.teamSqid = teamSqid;
existingData.storageType = storageType;
// Only present once Varify's tracking script has finished loading —
// don't overwrite a previously-saved id with nothing.
if (varify_anonymousId) {
existingData.anonymousId = varify_anonymousId;
}
await browser.localStorage.setItem(
'varify-data',
JSON.stringify(existingData),
);
});
// How long an experiment exposure counts toward a later purchase.
const WINDOW_MS = 28 * 24 * 60 * 60 * 1000;
const pruneEntries = (entries) => {
const threshold = Date.now() - WINDOW_MS;
return (entries || []).filter(
(entry) =>
entry.timestamp && entry.experimentId && entry.timestamp >= threshold,
);
};
// Fired by Shopify whenever a product is added to the cart (online store
// pages, not checkout). Sent as "add_to_cart" so it can be used as a goal.
analytics.subscribe('product_added_to_cart', async (event) => {
const existingData = await getVarifyData();
if (!existingData.data || existingData.data.length === 0) return;
existingData.data = pruneEntries(existingData.data);
if (existingData.data.length === 0) return;
const line = event.data?.cartLine;
const productId = line?.merchandise?.product?.id?.split('/').pop();
const variantId = line?.merchandise?.id?.split('/').pop();
await sendVarifyEvent(
'add_to_cart',
existingData.teamSqid,
existingData.anonymousId,
{
...flattenExperiments(existingData.data),
product_id: productId,
variant_id: variantId,
quantity: line?.quantity,
value: line?.cost?.totalAmount?.amount,
currency: line?.cost?.totalAmount?.currencyCode,
},
);
});
analytics.subscribe('checkout_started', async (event) => {
const existingData = await getVarifyData();
if (!existingData.data || existingData.data.length === 0) {
return;
}
existingData.data = pruneEntries(existingData.data);
await browser.localStorage.setItem(
'varify-data',
JSON.stringify(existingData),
);
if (existingData.data.length > 0) {
const resolvedAnonymousId = await sendVarifyEvent(
'checkout_started',
existingData.teamSqid,
existingData.anonymousId,
flattenExperiments(existingData.data),
);
// Persist the fallback id, if used, so it doesn't need re-deriving.
if (
resolvedAnonymousId &&
resolvedAnonymousId !== existingData.anonymousId
) {
existingData.anonymousId = resolvedAnonymousId;
await browser.localStorage.setItem(
'varify-data',
JSON.stringify(existingData),
);
}
}
// Warm the rate while the customer is still typing.
const currency = event.data?.checkout?.currencyCode;
if (currency && currency !== SHOP_CURRENCY) {
getFxRate(currency);
}
});
analytics.subscribe('checkout_completed', async (event) => {
const orderId = event.data?.checkout?.order?.id;
const money = event.data?.checkout?.subtotalPrice;
let orderRevenue = money?.amount;
const orderCurrency = money?.currencyCode;
// 0 is a valid revenue (100% discount) and must not count as missing.
if (!orderId || orderRevenue == null || !orderCurrency) {
console.warn(
'Varify: checkout_completed missing orderId, revenue or currency.',
);
return;
}
// subtotalPrice is presentment currency and the event carries no rate or
// shop-money amount. Convert to shop currency here, or skip.
if (orderCurrency !== SHOP_CURRENCY) {
const rate = await getFxRate(orderCurrency);
if (!rate) {
console.warn(
`Varify: skipping order ${orderId} — ${orderRevenue} ${orderCurrency}, no FX rate available.`,
);
return;
}
const converted = +(orderRevenue / rate).toFixed(2);
console.info(
`Varify: converted order ${orderId} — ${orderRevenue} ${orderCurrency} @ ${rate} -> ${converted} ${SHOP_CURRENCY}.`,
);
orderRevenue = converted;
}
const parsedData = await getVarifyData();
if (!parsedData.data || parsedData.data.length === 0 || !parsedData.teamId) {
console.warn('Varify: no valid data in local storage.');
return;
}
// The order-status page refires checkout_completed on every visit.
if (!Array.isArray(parsedData.sentOrders)) {
parsedData.sentOrders = [];
}
if (parsedData.sentOrders.includes(orderId)) {
return;
}
parsedData.data = pruneEntries(parsedData.data);
if (parsedData.data.length === 0) {
return;
}
const lineItems = event.data?.checkout?.lineItems;
let cartProducts = [];
if (lineItems && Array.isArray(lineItems)) {
cartProducts = lineItems
.map((item) => {
const productId = item.variant?.product?.id?.split('/').pop();
const variantId = item.variant?.id?.split('/').pop();
if (productId && variantId) {
return {
productId,
variantId,
};
}
return null;
})
.filter((p) => p !== null);
}
const resolvedAnonymousId = await sendVarifyEvent(
'checkout_completed',
parsedData.teamSqid,
parsedData.anonymousId,
{
...flattenExperiments(parsedData.data),
order_id: String(orderId),
value: orderRevenue,
currency: SHOP_CURRENCY,
cart_product_ids: cartProducts.map((p) => p.productId).join(','),
cart_variant_ids: cartProducts.map((p) => p.variantId).join(','),
},
);
// Persist the fallback id, if used, so it doesn't need re-deriving.
if (resolvedAnonymousId) {
parsedData.anonymousId = resolvedAnonymousId;
}
parsedData.sentOrders = [...parsedData.sentOrders, orderId].slice(-50);
await browser.localStorage.setItem('varify-data', JSON.stringify(parsedData));
});
Kodu ekledikten sonra, üstteki menüde "Connect" seçeneğine tıkla. Shopify takibin artık kuruldu.
Shopify hedeflerini Varify'a ekleyin
Özel piksel yalnızca olayları gönderir. Bunları deneylerinde kullanmak için, önce her bir Shopify olayını Varify'da bir Custom Event Goal olarak kaydet. Bunu her hedef için yalnızca bir kez yapman gerekir. Daha sonra hedefleri herhangi bir deneyde seçebilirsin.
Hedef adını sen belirlersin. Ancak event name, pikselin gönderdiği olayın adıyla büyük/küçük harf ve alt çizgiler dahil tam olarak aynı olmalıdır. Aksi takdirde Varify olayları eşleştiremez ve hedef herhangi bir veri kaydetmez.
| Shopify olayı | Varify'daki olay adı (pikselin gönderdiği şekliyle tam olarak) | Örnek hedef adı |
|---|---|---|
product_added_to_cart | add_to_cart | Add to Cart |
checkout_started | checkout_started | Begin checkout |
checkout_completed | checkout_completed | Purchase |
Piksel, Shopify olayı product_added_to_cart'ı Varify'a add_to_cart olarak gönderir. Varify'da olay adı olarak add_to_cart kullan.
Adım 1: Yeni bir Custom Event Goal oluştur
- Sol taraftaki kenar çubuğundan „Goals“ bölümüne git ve yeni bir hedef oluştur.
- Hedef türü olarak „Custom event“ seçeneğini seç ve „Next“ üzerine tıkla.
Adım 2: Hedef adını ve olay adını gir
- İstediğin bir goal name gir, örneğin „Purchase.“ Bu ad raporlarda gösterilir.
- event name'i yukarıdaki tabloda gösterildiği gibi tam olarak gir, örneğin
checkout_completed.
Olay adı, hedef adından otomatik olarak doldurulur. Bunu tablodaki tam olay adıyla üzerine yaz. Daha sonra değiştirilemez, bu yüzden devam etmeden önce kontrol et.
Adım 3: Ne sıklıkla sayılacağını seç ve kaydet
- adımda, hedefin ne sıklıkla sayılması gerektiğini seç („Once per visitor“ veya „Every time“). „Purchase“ gibi normal bir dönüşüm hedefi için „Once per visitor“ seçeneğini koru. Ardından „Create goal“ üzerine tıkla.
Bu adımda gösterilen izleme kod parçacığı Shopify hedefleri için gerekli değildir, çünkü özel piksel olayı zaten gönderir.
Geliri ölç (ziyaretçi başına gelir)
Geliri analiz etmek için, örneğin ziyaretçi başına geliri, başka bir Custom Event Goal oluştur:
- Ona bir isim ver, örneğin „Revenue.“
- Event adı olarak
checkout_completedgir, „Purchase“ için kullandığınla aynı. -
- adımda „Track revenue“ seçeneğini aç („Measure order value alongside conversions“).
- „Create goal“e tıkla.
Hedefleri deneyine ekle
Custom Event Goals oluşturulduktan sonra, onları bir deneye ekleyebilirsin:
- Deneyini aç ve „Goals“ sekmesine git.
- „+ Custom Event Goal“e tıkla ve listeden hedefini seç, örneğin „Purchase“ veya „Revenue.“
- „Save Goals“e tıkla.
Hedef artık listede „Custom event“ olarak işaretlenmiş şekilde görünür.
Hedeflerini deneyi başlatmadan önce ekle. Sonradan eklenen hedefler geriye dönük olarak izlenmez.