Documentation Index
Fetch the complete documentation index at: https://docs.scalev.com/llms.txt
Use this file to discover all available pages before exploring further.
Storefront API menyediakan endpoint konversi server-side untuk Meta, TikTok, dan SnackVideo. Endpoint ini hanya berperan sebagai server relay. Storefront Anda tetap mengatur bagian browser: memuat pixel, menyimpan ID atribusi, memilih event konversi yang dikirim, membuat payload commerce, dan memakai event ID yang sama di browser pixel serta event server Scalev untuk deduplikasi.
Gunakan halaman ini saat storefront custom Anda dipakai untuk iklan.
Cara alurnya bekerja
Untuk setiap konversi yang ingin dilacak:
- Muat browser pixel dari platform iklan.
- Ambil ID atribusi dari URL landing dan cookie.
- Buat parameter event dan payload item.
- Buat satu event ID untuk Meta atau TikTok.
- Kirim event browser pixel dengan event ID tersebut.
- Kirim event yang sama ke endpoint analytics Storefront API Scalev.
Scalev kemudian mengirim event server-side ke pixel yang sudah dikonfigurasi di pengaturan Storefront analytics toko.
Route Storefront API memilih toko dari /v3/stores/{store_id} dan X-Scalev-Storefront-Api-Key. Jangan mengirim custom domain atau page ID untuk mengidentifikasi toko.
POST /v3/stores/{store_id}/public/analytics/meta/events
POST /v3/stores/{store_id}/public/analytics/tiktok/events
POST /v3/stores/{store_id}/public/analytics/snackvideo/events
Ketiga endpoint membutuhkan publishable Storefront API key:
const API_BASE = "https://api.scalev.com";
const STORE_ID = "store_xxx";
const STOREFRONT_KEY = "sfpk_xxx";
const analyticsPaths = {
meta: "/public/analytics/meta/events",
tiktok: "/public/analytics/tiktok/events",
snackvideo: "/public/analytics/snackvideo/events",
};
async function postScalevAnalytics(provider, payload) {
const response = await fetch(
`${API_BASE}/v3/stores/${STORE_ID}${analyticsPaths[provider]}`,
{
method: "POST",
credentials: "omit",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"X-Scalev-Storefront-Api-Key": STOREFRONT_KEY,
},
body: JSON.stringify(payload),
}
);
if (!response.ok) {
throw new Error(`Scalev analytics request failed: ${response.status}`);
}
}
Endpoint analytics mengembalikan 204 No Content setelah menerima event untuk dikirim secara asynchronous.
Konfigurasi browser pixel
Server relay tidak menggantikan browser pixel. Anda tetap membutuhkan browser pixel untuk atribusi, optimasi, dan deduplikasi Meta/TikTok.
Gunakan pixel ID yang sama dengan pixel yang dikonfigurasi di Scalev Storefront analytics. Pixel ID adalah identifier publik, jadi Anda dapat menyimpannya di konfigurasi frontend:
const META_PIXEL_IDS = ["1234567890"];
const TIKTOK_PIXEL_IDS = ["CABCDEF123456"];
const SNACKVIDEO_PIXEL_IDS = ["KWAI_PIXEL_ID"];
Muat SDK browser resmi setiap provider satu kali, lalu inisialisasi pixel yang dikonfigurasi:
function initPixels() {
if (window.__scalevStorefrontPixelsInitialized) return;
window.__scalevStorefrontPixelsInitialized = true;
for (const pixelId of META_PIXEL_IDS) {
window.fbq?.("init", pixelId);
}
window.fbq?.("track", "PageView");
for (const pixelId of TIKTOK_PIXEL_IDS) {
window.ttq?.load(pixelId);
}
window.ttq?.page?.();
for (const pixelId of SNACKVIDEO_PIXEL_IDS) {
window.kwaiq?.load(pixelId);
}
window.kwaiq?.page?.();
}
Jika Anda memakai Google Tag Manager atau tag manager lain, atur dengan prinsip yang sama: event browser dan event server Scalev harus memakai nama event dan event ID yang sama untuk satu aksi pembeli.
Izinkan domain storefront di Meta
Meta memakai Traffic permissions di Events Manager untuk mengatur domain mana yang boleh mengirim event lewat Meta pixel. Jika allow list Meta aktif dan domain storefront Anda tidak ada di daftar tersebut, Meta akan memblokir event dari domain itu. Pixel masih bisa terlihat firing di browser, dan Scalev masih bisa menerima request server relay, tetapi event tidak akan tercatat oleh Meta.
Selesaikan ini sebelum menjalankan iklan atau memvalidasi event produksi.
- Buka Meta Events Manager.
- Pilih pixel atau dataset yang dipakai oleh pengaturan Storefront analytics toko.
- Buka Settings.
- Di Traffic permissions, buat atau kelola allow list.
- Tambahkan domain storefront produksi yang dikunjungi pembeli, seperti
shop.example.com atau example.com.
- Konfirmasi perubahan, lalu uji event storefront nyata di Meta Test events.
Jika Anda menambahkan top-level domain seperti example.com, Meta juga mencakup subdomainnya, seperti shop.example.com. Jika Anda tidak ingin semua subdomain diterima, tambahkan subdomain yang spesifik satu per satu.
Jangan memasukkan domain staging, preview, atau localhost ke allow list pixel produksi kecuali event dari domain tersebut memang harus diterima oleh dataset produksi. Saat mengirim server event lewat Scalev, pastikan event_source_url memakai domain storefront yang sama dengan domain yang Anda izinkan di Meta.
Lihat dokumentasi Meta tentang pengelolaan pixel traffic permissions dan praktik terbaik traffic permissions.
Ambil ID atribusi
Ambil ad click ID segera saat app berjalan. Storefront bawaan Scalev menyimpan nilai berikut:
| Sumber | Query parameter | Cookie | Dipakai oleh |
|---|
| Meta click ID | fbclid | _fbc | Meta Conversions API |
| Meta browser ID | Dibuat saat belum ada | _fbp | Meta Conversions API |
| TikTok click ID | ttclid | _ttclid | TikTok Events API |
| TikTok browser ID | Dibuat oleh TikTok pixel | _ttp | TikTok Events API |
| SnackVideo click ID | click_id | _kwai_click_id | SnackVideo Events API |
Simpan click ID selama tujuh hari. Meta browser ID biasanya disimpan lebih lama karena browser pixel Meta memakainya lintas kunjungan.
function getCookie(name) {
return document.cookie
.split("; ")
.find((row) => row.startsWith(`${name}=`))
?.split("=")[1];
}
function setCookie(name, value, maxAgeDays) {
if (!value) return;
const secure = location.protocol === "https:" ? "Secure" : "";
document.cookie = [
`${name}=${encodeURIComponent(value)}`,
`Max-Age=${maxAgeDays * 24 * 60 * 60}`,
"Path=/",
"SameSite=Lax",
secure,
].filter(Boolean).join("; ");
}
export function captureAdAttribution() {
const params = new URLSearchParams(location.search);
const now = Date.now();
const fbclid = params.get("fbclid");
if (fbclid) {
setCookie("_fbc", `fb.1.${now}.${fbclid}`, 90);
}
if (!getCookie("_fbp")) {
const random = Math.floor(Math.random() * 10000000000);
setCookie("_fbp", `fb.1.${now}.${random}`, 90);
}
setCookie("_ttclid", params.get("ttclid"), 7);
setCookie("_kwai_click_id", params.get("click_id"), 7);
}
export function currentAttribution() {
const params = new URLSearchParams(location.search);
return {
fbc: decodeURIComponent(getCookie("_fbc") || ""),
fbp: decodeURIComponent(getCookie("_fbp") || ""),
ttclid:
params.get("ttclid") ||
decodeURIComponent(getCookie("_ttclid") || ""),
ttp: decodeURIComponent(getCookie("_ttp") || ""),
snackVideoClickId:
params.get("click_id") ||
decodeURIComponent(getCookie("_kwai_click_id") || ""),
};
}
Jalankan captureAdAttribution() sebelum Anda mengirim event page, produk, cart, checkout, atau order.
Pilih event
Kirim event setelah aksi pembeli berhasil. Jangan mengirim event konversi sebelum aksi API yang diwakili event tersebut selesai.
| Momen storefront | Kirim setelah | Event Meta | Event TikTok | Event SnackVideo |
|---|
| Detail produk atau bundle terlihat | Response API detail sudah dirender | ViewContent | ViewContent | Event content-view yang dikonfigurasi |
| Item ditambahkan ke cart | POST /public/cart/items berhasil | AddToCart | AddToCart | Event add-to-cart yang dikonfigurasi |
| Checkout dimulai | Halaman atau form checkout terbuka dengan konteks item | InitiateCheckout | InitiateCheckout | Event checkout yang dikonfigurasi |
| Pembeli memilih metode pembayaran | Payment selector dibuka atau metode dipilih | Event opsional yang dikonfigurasi | Event opsional yang dikonfigurasi | Event opsional yang dikonfigurasi |
| Order dibuat | POST /public/checkout atau POST /customers/me/checkout berhasil | Purchase atau event submit yang dikonfigurasi | CompletePayment atau event submit yang dikonfigurasi | Event purchase yang dikonfigurasi |
| Halaman payment success terlihat | Hanya jika Anda belum mengirim konversi order | Purchase | CompletePayment | Event purchase yang dikonfigurasi |
Pilih satu titik konversi untuk optimasi purchase. Jika Anda mengirim purchase saat order dibuat, jangan mengirim purchase lagi di halaman payment. Jika Anda hanya mengoptimasi order yang sudah dibayar, kirim purchase setelah konfirmasi pembayaran, bukan saat order dibuat.
Untuk TikTok, gunakan nama event yang dikonfigurasi di TikTok Events Manager. Untuk optimasi purchase, TikTok umumnya memakai CompletePayment.
Untuk SnackVideo, gunakan nama event yang persis dikonfigurasi untuk toko. Storefront API meneruskan nama event yang Anda kirim.
Buat payload item
Payload analytics Scalev memakai item unique ID, bukan numeric ID yang dipakai oleh input checkout item.
Untuk product variant:
{
"variant_unique_id": "variant_xxx",
"quantity": 1
}
Untuk bundle price option:
{
"bundle_price_option_unique_id": "bundle_price_option_xxx",
"quantity": 1
}
Buat payload item analytics dari produk, bundle price option, cart row, atau checkout row yang menjadi konteks event:
function buildAnalyticsItems(items) {
const variants = [];
const bundle_price_options = [];
for (const item of items) {
if (item.type === "variant") {
variants.push({
variant_unique_id: item.variant_unique_id,
quantity: item.quantity,
});
}
if (item.type === "bundle_price_option") {
bundle_price_options.push({
bundle_price_option_unique_id: item.bundle_price_option_unique_id,
quantity: item.quantity,
});
}
}
return {
variants: variants.length > 0 ? variants : undefined,
bundle_price_options:
bundle_price_options.length > 0 ? bundle_price_options : undefined,
};
}
Untuk event detail produk, kirim variant yang terlihat dengan quantity 1. Untuk event checkout atau purchase, kirim semua item di checkout.
Buat parameter event
Kirim parameter commerce yang ramah untuk setiap provider.
Untuk Meta, parameter umum adalah:
{
content_ids: ["variant_xxx"],
content_type: "product",
contents: [
{
id: "variant_xxx",
quantity: 1,
item_price: 150000,
delivery_category: "home_delivery",
},
],
content_name: "Black T-Shirt",
content_category: "Apparel",
value: 150000,
currency: "IDR",
num_items: 1,
}
Untuk TikTok, sertakan content_id yang stabil jika memungkinkan. Storefront bawaan mengambilnya dari konteks produk, bundle, atau halaman saat payload event tidak menyertakannya.
{
content_id: "product_or_bundle_xxx",
content_type: "product",
contents: [
{
content_id: "variant_xxx",
quantity: 1,
price: 150000,
},
],
value: 150000,
currency: "IDR",
}
Untuk SnackVideo, kirim properti seperti:
{
content_type: "product",
content_category: "Apparel",
content_name: "Black T-Shirt",
currency: "IDR",
value: 150000,
quantity: 1,
price: 150000,
}
Gunakan format integer yang sama dengan total IDR yang Anda tampilkan di storefront.
Meta dan TikTok melakukan deduplikasi memakai event ID. Buat satu event ID untuk satu aksi pembeli, lalu pakai ulang di dua tempat:
- Meta browser pixel:
eventID
- Payload Meta Storefront API:
events[].event_id
- TikTok browser pixel:
event_id
- Payload TikTok Storefront API:
events[].event_id
Untuk event page, produk, cart, dan interaksi checkout, buat event ID random:
function newEventId(prefix = "evt") {
if (crypto.randomUUID) return `${prefix}_${crypto.randomUUID()}`;
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
}
Untuk event submit order, pakai ID deterministik berdasarkan order. Ini mencegah purchase dobel jika pembeli retry request atau reload halaman success.
function orderEventId(order, eventName) {
return `${order.order_id}-${eventName}-FORM`;
}
Payload Storefront API SnackVideo saat ini tidak memakai event_id. Untuk SnackVideo, pakai click_id untuk atribusi dan pastikan frontend tidak mengirim konversi yang sama dua kali.
async function trackMetaEvent({
eventName,
eventId,
parameters,
userData = {},
items = {},
}) {
const attribution = currentAttribution();
window.fbq?.("track", eventName, parameters, {
eventID: eventId,
});
await postScalevAnalytics("meta", {
event_source_url: location.href,
referrer_url: document.referrer || undefined,
user_data: {
country: "id",
fbc: attribution.fbc || undefined,
fbp: attribution.fbp || undefined,
...userData,
},
events: [
{
event_id: eventId,
event_name: eventName,
parameters,
},
],
...items,
});
}
Meta user data dapat berisi fn, ln, em, ph, ct, st, country, external_id, location_id, fbc, dan fbp. Scalev mengisi client_ip_address dan client_user_agent dari request API yang diterima, jadi Anda tidak perlu mengirimnya dari browser.
Kirim event TikTok
async function trackTiktokEvent({
eventName,
eventId,
parameters,
user = {},
items = {},
}) {
const attribution = currentAttribution();
window.ttq?.track(eventName, parameters, {
event_id: eventId,
});
await postScalevAnalytics("tiktok", {
event_source_url: location.href,
referrer_url: document.referrer || undefined,
user: {
ttclid: attribution.ttclid || undefined,
ttp: attribution.ttp || undefined,
...user,
},
events: [
{
event_id: eventId,
event: eventName,
parameters,
},
],
...items,
});
}
TikTok user data dapat berisi email, phone, external_id, ttclid, dan ttp. Scalev mengisi ip dari request API yang diterima dan memakai user_agent dari request saat Anda tidak mengirimkannya.
Kirim event SnackVideo
async function trackSnackVideoEvent({
eventName,
properties,
items = {},
}) {
const attribution = currentAttribution();
for (const pixelId of SNACKVIDEO_PIXEL_IDS) {
window.kwaiq?.instance?.(pixelId)?.track(eventName, properties);
}
if (!attribution.snackVideoClickId) return;
await postScalevAnalytics("snackvideo", {
click_id: attribution.snackVideoClickId,
event_source_url: location.href,
referrer_url: document.referrer || undefined,
events: [
{
event_name: eventName,
properties,
},
],
...items,
});
}
Server event SnackVideo membutuhkan click_id. Jika pembeli tidak datang dengan click_id, tetap kirim browser pixel event saat dikonfigurasi, tetapi lewati server event.
Contoh: add to cart
Kirim ini setelah POST /public/cart/items berhasil:
async function trackAddToCart(cartItem) {
const item = {
type: "variant",
variant_unique_id: cartItem.variant.unique_id,
quantity: cartItem.quantity,
};
const items = buildAnalyticsItems([item]);
const parameters = {
content_ids: [item.variant_unique_id],
content_type: "product",
contents: [
{
id: item.variant_unique_id,
quantity: item.quantity,
item_price: cartItem.unit_price,
},
],
value: cartItem.unit_price * cartItem.quantity,
currency: "IDR",
num_items: item.quantity,
};
const eventId = newEventId("add_to_cart");
await Promise.all([
trackMetaEvent({
eventName: "AddToCart",
eventId,
parameters,
items,
}),
trackTiktokEvent({
eventName: "AddToCart",
eventId,
parameters: {
...parameters,
content_id: item.variant_unique_id,
},
items,
}),
trackSnackVideoEvent({
eventName: "addToCart",
properties: parameters,
items,
}),
]);
}
Contoh: konversi checkout
Kirim konversi purchase setelah checkout berhasil, kecuali strategi iklan Anda hanya menghitung order yang sudah dibayar.
async function trackOrderCreated(order, checkoutItems, buyer) {
const items = buildAnalyticsItems(checkoutItems);
const value = order.gross_revenue || order.total_price;
const parameters = {
content_ids: checkoutItems.map(
(item) => item.variant_unique_id || item.bundle_price_option_unique_id
),
content_type: "product",
contents: checkoutItems.map((item) => ({
id: item.variant_unique_id || item.bundle_price_option_unique_id,
quantity: item.quantity,
item_price: item.unit_price,
})),
value,
currency: "IDR",
num_items: checkoutItems.reduce((total, item) => total + item.quantity, 0),
};
await Promise.all([
trackMetaEvent({
eventName: "Purchase",
eventId: orderEventId(order, "Purchase"),
parameters,
userData: {
em: buyer.email,
ph: buyer.phone,
fn: buyer.firstName,
ln: buyer.lastName,
ct: buyer.city,
st: buyer.province,
external_id: String(order.customer_id || buyer.customerId || ""),
},
items,
}),
trackTiktokEvent({
eventName: "CompletePayment",
eventId: orderEventId(order, "CompletePayment"),
parameters: {
...parameters,
content_id: parameters.content_ids[0],
},
user: {
email: buyer.email,
phone: buyer.phone,
external_id: String(order.customer_id || buyer.customerId || ""),
},
items,
}),
trackSnackVideoEvent({
eventName: "purchase",
properties: parameters,
items,
}),
]);
}
Jika response order atau pengecekan fraud Anda menandai order sebagai spam, lewati event konversi purchase.
Referensi payload
Request Meta:
{
"event_source_url": "https://shop.example.com/products/black-shirt",
"referrer_url": "https://facebook.com/",
"user_data": {
"country": "id",
"em": "buyer@example.com",
"ph": "628123456789",
"fbc": "fb.1.1710000000000.fbclid",
"fbp": "fb.1.1710000000000.1234567890",
"external_id": "customer_123"
},
"events": [
{
"event_id": "add_to_cart_123",
"event_name": "AddToCart",
"parameters": {
"content_ids": ["variant_xxx"],
"content_type": "product",
"value": 150000,
"currency": "IDR"
}
}
],
"variants": [
{
"variant_unique_id": "variant_xxx",
"quantity": 1
}
]
}
Request TikTok:
{
"event_source_url": "https://shop.example.com/products/black-shirt",
"referrer_url": "https://tiktok.com/",
"user": {
"email": "buyer@example.com",
"phone": "628123456789",
"ttclid": "ttclid_value",
"ttp": "ttp_cookie_value",
"external_id": "customer_123"
},
"events": [
{
"event_id": "add_to_cart_123",
"event": "AddToCart",
"parameters": {
"content_id": "variant_xxx",
"content_type": "product",
"value": 150000,
"currency": "IDR"
}
}
],
"variants": [
{
"variant_unique_id": "variant_xxx",
"quantity": 1
}
]
}
Request SnackVideo:
{
"click_id": "snackvideo_click_id",
"event_source_url": "https://shop.example.com/products/black-shirt",
"referrer_url": "https://snackvideo.com/",
"events": [
{
"event_name": "addToCart",
"properties": {
"content_type": "product",
"content_name": "Black T-Shirt",
"value": 150000,
"currency": "IDR",
"quantity": 1
}
}
],
"variants": [
{
"variant_unique_id": "variant_xxx",
"quantity": 1
}
]
}
Checklist produksi
- Muat browser pixel satu kali per page session.
- Ambil
fbclid, ttclid, dan click_id sebelum mengirim event.
- Pertahankan
_fbp, _fbc, _ttp, _ttclid, dan _kwai_click_id.
- Tambahkan domain storefront produksi ke Meta pixel traffic allow list sebelum memvalidasi event Meta.
- Kirim event hanya setelah aksi API yang diwakili event tersebut berhasil.
- Pakai satu event ID untuk copy browser dan server dari setiap event Meta atau TikTok.
- Pakai event ID deterministik untuk event purchase.
- Kirim
event_source_url dan referrer_url agar Scalev dapat memilih konfigurasi checkout atau home storefront analytics.
- Kirim
variants atau bundle_price_options dengan item unique ID dan quantity.
- Lewati server event SnackVideo saat tidak ada
click_id.
- Jangan menahan checkout jika request analytics gagal. Log error tersebut dan biarkan pembeli melanjutkan.