Skip to content

Pixel

The slimTDS pixel is a lightweight JavaScript snippet that identifies visitors on external lander pages, records page events, and ties those events back to clicks routed through the TDS. It is built on top of FingerprintJS Community Edition.

Pixel events feed in the slimTDS admin

GET /p.js serves the compiled pixel script (a Bun build artifact at public/p.js). When the script loads it automatically generates a FingerprintJS CE visitor ID and fires a pageview event to /p/event. The script is served with a 5-minute public cache (Cache-Control: public, max-age=300, stale-while-revalidate=60) and standard ETag-based 304 handling, so repeat loads are fast.

Every campaign has a pixel page at /admin/campaigns/{cid}/pixel. That page shows the embed snippet and a copy button. The snippet shape is:

<script async src="https://your-domain.com/p.js?c=CAMPAIGN_SLUG"></script>

Place the tag in the <head> or <body> of each lander page. The ?c= query parameter is the campaign slug — it is required and ties events to the correct campaign.

After the script initialises, window.slimTDS.track() is available for custom events. The pixel page shows this example:

window.slimTDS && slimTDS.track('purchase', { amount: 99, sku: 'X1' })

The first argument is the event name; the second is an arbitrary props object that will be stored verbatim in the props JSONB column of stats.pixel_events.

Alongside events, the pixel records the visit with rrweb and streams the recording to POST /p/rec in chunks. These become watchable replays in the admin — see Session Replays.

Events are received at POST /p/event. The body can be JSON (Content-Type: application/json) or form-encoded. Accepted fields:

FieldTypeNotes
cstringCampaign slug — required
eventstringEvent name (default: pageview)
fpstringFingerprintJS CE visitorId
urlstringCurrent page URL
refstringReferrer URL
uastringUser-Agent (overrides header if set)
langstringBrowser language
tzstringTimezone string
swintScreen width
shintScreen height
tintClient-side Unix timestamp (informational)
propsobjectArbitrary custom properties

Responses: 400 if c is missing, 404 if the campaign slug is unknown, 204 on success. A Set-Cookie: vu=… header is included when the request belongs to a new visitor.

/p/event is designed to be called from any external lander domain. The CORS behavior is:

  • An OPTIONS preflight to /p/event returns 204 with permissive headers.
  • For actual POST requests, the response echoes the request Origin header back in Access-Control-Allow-Origin (rather than *) so the response is valid when the browser sends credentials — this is necessary because navigator.sendBeacon with a typed Blob payload triggers a credentialed request.
  • When there is no Origin header, Access-Control-Allow-Origin: * is used.
  • Access-Control-Allow-Credentials: true is added when an Origin is present.
  • Access-Control-Max-Age: 86400 (24 hours) reduces preflight round-trips.
  • Allowed methods: POST, OPTIONS. Allowed headers: Content-Type.

The script itself (GET /p.js) is also served with Access-Control-Allow-Origin: *.

The request handler writes a raw payload row to stats.pixel_events_inbox — an UNLOGGED staging table — and returns immediately. No GeoIP lookup or UA parsing happens in the request path.

The inbox:flush cron command (schedule: * * * * *, every minute) runs a loop inside a single process for ~55 seconds. On each iteration it reads up to 500 rows from the inbox, enriches each one with GeoIP data (MaxMind GeoLite2) and device/OS/browser detection, runs bot detection, then inserts into the partitioned stats.pixel_events table and deletes the processed inbox rows. Because the command re-polls every 5 seconds when the inbox is empty, events typically surface in the admin within 5–6 seconds of arrival.

As a side effect of the drain, if the pixel event carries a FingerprintJS visitorId and the visitor had a recent click logged before the inbox was flushed, inbox:flush backfills fp_js onto those click rows so the two identities are joined retroactively.