Server-Side Conversions
Some conversions don’t happen in the browser. An inquiry only turns into a paid order days later, a subscription activates after the trial period ends, an order is only approved after a credit check.
With the server API, you report such events from your own backend to usertrax. usertrax finds the original visit again and carries over its ad attribution — gclid, fbclid, UTM parameters — onto the new event. That way the revenue lands in Google Ads and Meta on the campaign that triggered it.
Save a reference when the form is submitted
The tracker gives you the IDs of the current visit:
const ids = window.usertrax.getTrackingIds();// { session_id: "…", visitor_id: "…" }Write
session_idinto a hidden form field and store the value together with the inquiry in your database:<form method="post" action="/inquiry"><input type="hidden" name="usertrax_session" id="usertrax_session" /><!-- other fields --></form><script>document.getElementById("usertrax_session").value =window.usertrax.getTrackingIds().session_id || "";</script>Get your server key
In the dashboard under Domains → Settings you’ll find the server key (
utx_sk_…).The server key is secret. It belongs exclusively in your backend — never in JavaScript, HTML, or a public repository. It is not the same value as the auth key from the tracking script.
Report the conversion
As soon as the inquiry turns into an order, your backend sends:
Terminal-Fenster curl -X POST https://api.usertrax.io/api/v1/server/conversions \-H "X-Server-Key: utx_sk_..." \-H "Content-Type: application/json" \-d '{"event": "purchase","event_id": "order-8123","value": 2500.00,"currency": "EUR","reference": { "session_id": "the-stored-session-id" },"user_data": { "email": "customer@example.com" }}'Response:
{"status": "success","conversion_id": 91823,"event_id": "order-8123","attribution_source": "session"}attribution_sourceshows how the link was found. If it saysnone, usertrax couldn’t match the original visit — the conversion is still saved, but without ad attribution.
Matching without a stored session ID
Section titled “Matching without a stored session ID”usertrax tries several paths and takes the first match:
| Field | Description |
|---|---|
reference.session_id | The most reliable option — see above |
reference.event_id | The event_id of the original conversion, e.g. your lead event |
reference.visitor_id | The recognizable visitor across multiple sessions |
user_data.email | Matched to an existing profile |
You can also pass gclid, fbclid, and utm directly if you’ve stored them yourself. Directly passed values always take precedence over inherited ones.
Multiple conversions at once
Section titled “Multiple conversions at once”For nightly reconciliation there’s a batch endpoint with up to 100 events per request:
curl -X POST https://api.usertrax.io/api/v1/server/conversions/batch \ -H "X-Server-Key: utx_sk_..." \ -H "Content-Type: application/json" \ -d '{ "conversions": [ { "event": "purchase", "event_id": "order-1", "value": 1200 }, { "event": "purchase", "event_id": "order-2", "value": 890 } ] }'The response reports back each item individually. A faulty record doesn’t block the rest.
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
event | string, required | Event name, e.g. purchase (max. 100 characters) |
event_id | string | Your own id, e.g. the order number. Used for duplicate detection |
value | number | Conversion value |
currency | string | ISO code, e.g. EUR. Defaults to the domain’s currency |
event_timestamp | string | Time in ISO 8601 format. Defaults to now. Max. 90 days in the past |
label | string | Free-form label for grouping in the dashboard |
reference.session_id | string | Session id from getTrackingIds() |
reference.event_id | string | event_id of the original conversion |
reference.visitor_id | string | Visitor id from getTrackingIds() |
user_data | object | email, customer_id, phone — for profile matching and enhanced conversions |
meta_data | object | Any additional data to store with the event |
gclid, fbclid, fbc, fbp, msclkid, ttclid, adcell, awc, li_fat_id | string | Click ids, if you stored them yourself |
utm | object | utm_source, utm_medium, utm_campaign, utm_term, utm_content |
landing_page | string | Entry page of the original visit |
first_touchpoint, last_touchpoint | string | Channel of the first and last touch |
ip_address, user_agent | string | IP and user agent of the original visit, not your server’s. Improves Meta matching |
Every field except event is optional. Anything you leave out, usertrax fills in from the matched visit.
Good to know
Section titled “Good to know”- Duplicate reports are caught via
event_id. Feel free to send the same order twice — the response then readsduplicate, and no second conversion is created. - Backdating is possible up to 90 days (
event_timestampin ISO 8601 format). usertrax rejects older events, since Google Ads and Meta would discard them anyway. - Existing events stay unchanged. The inquiry doesn’t get an order “rewritten” onto it — an additional event is created instead. Your funnel stays visible.
- The endpoints are also listed in the API reference under “Server-Side Tracking”.