Server-Side Events (S2S)
The browser pixel (s.js) is the easy way to record
on-page events, but it’s structurally blockable: Brave, ad blockers, and privacy
modes drop it, and it can’t run at all inside a native mobile app. Server-side
events are the durable alternative — your own backend sends the event straight to
Synaptyx over HTTPS, authenticated with an API key.
It’s the server-to-server counterpart of the pixel, and it lands in the same pixel-event store — so these events show up in your funnels, journeys, and source analytics next to the browser ones.
Server-side events never create a conversion. They are best-effort analytics
signals only. Revenue stays attributed exclusively through your
Revenue Sources (Stripe / Polar) and
CPA postbacks, which are signature-verified and can’t be
inflated by a caller. The value you send here is an unverified signal, not money.
Server-side events are included, not billed — only tracked clicks count toward your
plan’s allowance. They do sit under a fair-use ceiling (20x your click allowance); past
it, accepted: true, recorded: false is returned with reason: "fair_use_ceiling"
until the next month.
When to use it
- Mobile apps — no browser, no pixel. Send
signup,login,checkoutfrom your app’s backend. - Ad-blocked web traffic — mirror your key pixel events server-side so they survive Brave / uBlock.
- Backend-only milestones — events that only your server knows about (trial converted, subscription renewed, KYC passed…).
Authentication & scope
Send the event with an API key carrying the write:events
scope (the only write scope on the v1 API — available on Pro and Scale).
curl -X POST https://app.synaptyx.pro/api/v1/events \
-H "Authorization: ApiKey syx_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "click_id": "V1StGXR8_Z5jdHi6B-myT", "event_name": "checkout", "value": 49.9, "currency": "EUR" }'Create a key in Settings → API Keys and tick write:events
(under Advanced).
The endpoint
| Method | Path | Scope |
|---|---|---|
POST | /api/v1/events | write:events |
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
click_id | string | one of | The Synaptyx click ID this event belongs to — the attribution key for paid traffic. See Carrying the attribution key. |
visitor_id | string | one of | The pixel’s visitor ID — the attribution key for organic / direct traffic, which has no click. Read it from the first-party _syx_vid cookie. Requires campaign_id. |
campaign_id | string | with visitor_id | The campaign the event belongs to — the ID already in your pixel snippet URL. Ignored when click_id is supplied (the click decides). |
event_name | string | yes | ^[a-zA-Z][a-zA-Z0-9_]{0,63}$. e.g. login, signup, checkout. |
value | number | no | A numeric signal value (not revenue). Finite; clamped to ±1e12. |
currency | string | no | ISO-4217, uppercase (EUR, USD). Ignored if malformed. |
properties | object | no | Custom fields. Max 20 keys, primitive values only (string ≤256 chars / finite number / boolean). Anything else is silently dropped. |
event_id | string | no | Idempotency key — a repeat within 24h is accepted as a no-op. |
timestamp | string | no | ISO-8601 event time. Defaults to now; clamped to [now − 30d, now + 5m]. |
Which attribution key to send
An event has to attach to something. Send whichever key you have:
| Traffic | Key | Why |
|---|---|---|
| Paid (a Synaptyx link was clicked) | click_id | Most precise — ties the event to the exact click, its cost and its source. |
| Organic / SEO / direct | visitor_id + campaign_id | There was no click, so there is no click ID. The pixel’s visitor ID is the next-best stable key. |
With a click_id, Synaptyx resolves the click → its campaign, and the campaign must
belong to your account. campaignId / userId are derived from the resolved click,
never from your payload, so you can’t inject events onto someone else’s traffic — and a
campaign_id in the body is ignored. With a visitor_id, you name the campaign and we
check you own it before accepting anything.
An ID that doesn’t exist and one that isn’t yours return the same 404
(click_not_found / campaign_not_found) — the API never reveals whether another
account’s click or campaign exists.
Carrying the attribution key
The whole game is getting the key from the acquisition moment to the place that fires the event. Four patterns:
Web (SaaS / e-commerce)
The click ID arrives on your landing page as a URL param (?clickid=…) or the
first-party _syx_cid cookie. Persist it on the user’s record / order when they
sign up or check out, then replay it from your backend:
landing ?clickid=… ──▶ store on user/order ──▶ POST /api/v1/events { click_id, … }This is the same mechanism the Revenue Sources snippet uses.
Validation, limits & abuse controls
- Strict shapes — the IDs and
event_nameare regex-validated; a bad value returns400(it is never stored loosely). propertiessanitisation — over-limit keys, non-primitive values, and oversized blobs are dropped, but the event still goes through with the valid fields.- Idempotency — pass
event_idto make retries safe; a duplicate within 24h is a no-op. - Rate limits — per API key (standard
X-RateLimit-*headers, see Limits) and a per-identifier cap, so one click or visitor can’t be spammed with events.
Responses
| Status | Meaning |
|---|---|
200 | Accepted (body: { "accepted": true }, or { "accepted": true, "deduplicated": true }). |
400 | invalid_event_name, invalid_click_id, invalid_visitor_id, invalid_campaign_id, or missing_identifier (neither click_id nor visitor_id supplied). |
403 | insufficient_scope — the key lacks write:events. |
404 | click_not_found / campaign_not_found — unknown or not-owned ID. |
429 | rate_limited — back off and honour Retry-After. |
Examples
cURL
curl -X POST https://app.synaptyx.pro/api/v1/events \
-H "Authorization: ApiKey syx_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"click_id": "V1StGXR8_Z5jdHi6B-myT",
"event_name": "checkout",
"value": 49.9,
"currency": "EUR",
"event_id": "order_8421",
"properties": { "plan": "pro", "seats": 3 }
}'Organic visitor (no click) — same call, keyed on the _syx_vid cookie:
curl -X POST https://app.synaptyx.pro/api/v1/events \
-H "Authorization: ApiKey syx_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"visitor_id": "k3n8v1qwz9r2mx7cms4ussbe",
"campaign_id": "cmnvqgg5n03vo01n0as05a0n1",
"event_name": "signup"
}'Server-side events vs. conversions
| Server-side events | Conversions | |
|---|---|---|
| Endpoint | POST /api/v1/events | Revenue webhooks / CPA postbacks |
| Trust | Unverified signal | Signature-verified |
| Creates revenue? | No | Yes |
| Use for | Funnel steps, engagement, milestones | Money, payouts, ROI |
| Survives ad blockers | Yes (server-side) | Yes (server-side) |