Deduplication

How Skryx makes pixel + server-side events idempotent — counted once, observed everywhere.

Deduplication

The pattern: fire the same event from the pixel AND from your server. Skryx dedupes by event_id. Each event is counted once. You get "server-side accuracy + browser-side reach" without doubling counts.

This is Meta's Conversions API + Pixel pattern. Same logic, here.

# The dedup key: event_id

Every event carries an event_id chosen by the sender. Within a single tenant, the same event_id is processed at most once regardless of:

  • Which channel sent it first (pixel vs server)
  • How many times it was retried (network failure, queue replay)
  • Which day it falls under (dedup windows are 48h)

# Why dedup matters

Take order.completed:

  • The pixel fires it on the thank-you page — fastest, but lossy (user closes the tab before the page loads, ad-blocker, browser back button, etc.).
  • Your server fires it from the order webhook — slower (real-time attribution lags by seconds), but never lost.

If you fire both, you'd double-count every order without dedup. With dedup, accuracy = server-side, reach = pixel-side.

# Implementing the pixel + server combo

The recipe is "same event_id from both sides":

event_id = "ord_" + order.id   // stable, deterministic

# Pixel side

// On the thank-you page
skryx.track('order.completed', {
  order_id: '42',
  total: 199.99,
  // your pixel-side fields here
});

By default the pixel auto-generates event_id = "evt_…". For combo dedup, override it in properties.event_id_override, or issue the event manually with a known event_id. The simplest combined pattern:

<!-- On thank-you page server-rendered HTML -->
<script>
  skryx.track('order.completed', {
    event_id: 'ord_42',          // shared with server-side call
    order_id: '42',
    total: 199.99
  });
</script>

# Server side

POST https://api.skryx.io/v1/events
{
  "events": [{
    "event_id": "ord_42",          ← SAME id
    "event_name": "order.completed",
    "source": "server",
    "occurred_at": "...",
    "session_id": "...",
    "properties": { "order_id": "42", "total": 199.99, ... }
  }]
}

First side wins. The other side sees:

  • 204 No Content (pixel) or accepted: 0, rejected: 0 (server)
  • A bump on the deduped_24h counter in the dashboard

# What "dedup" actually does

  1. Pixel + server both POST. Whichever arrives first → processed.
  2. The second arrival's event_id is recognised and the event is dropped instead of being counted again.
  3. The drop is reflected in the dashboard's "Dedup rate" card so you can verify the combo is working.

# Operational details

  • Dedup window: 48 hours. Plenty for pixel + server retries.
  • Server-side guarantee: dedup is enforced at multiple layers, so even under failure conditions duplicates do not slip through.
  • No retries needed on your end: if you POST the same batch twice in a row (network jitter), the second pass is a no-op.

# Verifying it works

After a few real events, check /tracking:

  • Dedup rate card shows percentage of duplicates dropped.
  • Live feed shows each unique event once — duplicates never appear here.
  • By source counters split into pixel and server columns. For order events with the combo, you'll typically see pixel ≈ 70%
    • server = 100% (because dropping the pixel-version when the server arrived first counts toward the server, not the pixel).

# Anti-patterns

  • Don't randomize event_id between pixel and server — defeats dedup.
  • Don't use the same event_id for different events — only matches when both event_name + event_id match. Different event types share the same dedup namespace.
  • Don't retry forever on 429: respect quota_exceeded, back off until tomorrow.
esc