Overview
POST confirmed conversions from your backend. Bearer-token auth, ad-blocker immune, credited to the real visitor.
For SaaS apps where the success happens after a server-side confirmation — a payment captured, a signup verified, a booking confirmed — send it from your backend instead of the browser SDK. Ad blockers can't touch your backend traffic.
Get an API key
In the dashboard, add a success and choose "Track it from your server". Click Create API key and copy the token — it's shown once, so store it in your secrets manager. Need more than one (say, a separate key per service)? Use Add another key to create named extras.
The token looks like:
dl_live_<key_id>_<secret>Cap of 10 active keys per site. Revoke and rotate any time.
Pick your framework
Each guide is a copy-paste example that reads the visitor's request and forwards it, so the conversion is credited to the real person — device, country, and source included.
Backend frameworks
💎 Ruby on Rails
Fire a server-side conversion from a Rails controller action, crediting the real visitor:
🐍 Django
Record a server-side conversion from a Django view:
🧪 Flask
Fire a server-side conversion from a Flask route, crediting the real visitor:
⚙️ FastAPI
Fire a server-side conversion from a FastAPI route handler:
🟩 Express
Fire a server-side conversion from an Express route handler, crediting the real visitor:
🐈 NestJS
Fire a server-side conversion from a NestJS controller, crediting the real browser visitor:
🅻 Laravel (PHP)
Fire a server-side conversion from a Laravel route, crediting the real visitor:
🎼 Symfony (PHP)
Fire a server-side conversion from a Symfony controller, forwarding the visitor's request:
🔷 ASP.NET Core
Fire a server-side conversion from a minimal API endpoint:
☕ Spring Boot
Fire a server-side conversion from a Spring Boot @RestController, forwarding the visitor's request:
🐹 Go (stdlib / Gin / Echo)
Fire a server-side conversion from a Go net/http handler, crediting the real visitor:
🟣 Phoenix (Elixir)
Send a conversion from a Phoenix controller action, crediting the real visitor:
Meta-frameworks
▲ Next.js
Record a server-side conversion from a Next.js 16 Server Action:
🟢 Nuxt 3 (Vue)
Send a server-side conversion from a Nuxt server route (Nitro/H3):
💿 Remix / React Router
Fire a server-side conversion from a Remix action:
🔥 SvelteKit
Fire a server conversion from a SvelteKit +server.ts route handler:
🚀 Astro
Send a server-side conversion from an Astro API route:
⚡ Qwik City
Fire a server conversion from a Qwik City routeAction$:
🧬 SolidStart
Send a conversion from a SolidStart API route handler:
Not listed? Any backend works — the contract below is just an authenticated POST. Use the example closest to your HTTP client.
The endpoint
POST /event
Authorization: Bearer dl_live_<key_id>_<secret>
Content-Type: application/json
{
"event_name": "success",
"name": "signup",
"page_path": "/checkout/success",
"ip": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; …) Chrome/120",
"success_value": 9.99,
"properties": {
"plan": "starter"
}
}Returns 204 No Content on success. Returns 401 if the token is invalid or revoked.
Fields
| Field | Required | Notes |
|---|---|---|
event_name | Yes | One of pageview, click, success, custom |
page_path | Yes | Path label for journey/funnel analysis — needn't be a real page |
name | Recommended | The success rule name — without it the conversion can't match a named success |
ip + user_agent | Recommended | The end user's IP + browser, forwarded from their request. Credits the conversion to the real visitor (see below). Hashed and discarded — never stored |
success_user_id | Optional | Your own user id — groups a user's events, and is the webhook stitch key (see below). Not our visitor id |
success_value | Optional | Numeric value (e.g. revenue) |
properties | Optional | Flat object. Values must be string/number/boolean |
Credit the conversion to the visitor
A server-side success fires from your backend, which doesn't inherently know which browser visitor did it. There are two ways to connect it — pick by where the conversion happens.
In a request handler (recommended)
If the conversion runs while handling the user's request — a Server Action, a route handler — forward the visitor's IP and User-Agent from that request:
{ "event_name": "success", "name": "...", "page_path": "/...", "ip": "<visitor ip>", "user_agent": "<visitor ua>" }We hash them exactly the way the browser SDK does, so the event lands on the same visitor — inheriting their device, country, and source. No identify(), no ids, no cookie. The framework guides above do this for you.
Forward both ip and user_agent, and pass the User-Agent through unchanged — we match it byte-for-byte, so any rewriting breaks the link. The forwarded IP is used only to compute the visitor hash, then discarded — never stored or logged.
From a webhook (no user request)
A Stripe webhook, a cron job, or any background task has no user request to forward. There, link by id instead:
- In the browser, when the user is known, call
analytics.identify("user_123")— a one-shot ping, no cookie. - From your server, send that same id as
success_user_id.
We map the two within a 14-day window. Call identify() with a stable id (the same string for a given user), not in a loop — each new id fires one ping. Send success_user_id but never call identify(), and the conversions still group together under that id — just without device, country, or source. Skip both paths entirely and the success still counts, as a standalone visitor.
Rate limits
500 requests per 10 seconds per key, sliding window. 429 with Retry-After if you exceed. Bump the limit if you need more — email us.
PII safety
Same as the browser SDK — property keys matching email, phone, password, ssn are stripped server-side. Don't worry about accidentally including them.
What's next
- Frontend events? See the SDK reference.
- Want ad blockers to literally not see us? Set up the first-party proxy.