Server-Side Events
Send events from your backend to TraceLog using the server-to-server (S2S) webhook API. Use it to track backend outcomes like purchases, signups, subscriptions, refunds, or any event that originates outside the browser.
Endpoint
POST /webhooks/events
Base URL: https://api.tracelog.io
Authentication
Include your project API key in the x-api-key header:
x-api-key: your-project-api-key
You can find your API key in the TraceLog dashboard under Project Settings.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
event_id | string | Yes | Unique event ID, used as idempotency key |
event_name | string | Yes | Event name (e.g., purchase, signup) |
timestamp | string | Yes | ISO 8601 timestamp (must not be in the future) |
customer_id | string | No | Your system's user identifier |
session_id | string | No | TraceLog browser session ID for attribution linking |
referrer | string | No | Attribution source (fallback when session has no attribution) |
utm | object | No | UTM parameters (fallback when session has no attribution) |
metadata | object | No | Arbitrary key-value payload |
UTM Object
{
"source": "google",
"medium": "cpc",
"campaign": "spring-sale",
"term": "analytics",
"content": "banner-a"
}
Examples
Stripe Purchase
curl -X POST https://api.tracelog.io/webhooks/events \
-H "Content-Type: application/json" \
-H "x-api-key: your-project-api-key" \
-d '{
"event_id": "pi_3abc123",
"event_name": "purchase",
"timestamp": "2026-03-15T10:30:00Z",
"customer_id": "cus_xyz",
"session_id": "tl_sess_abc123",
"metadata": {
"amount": 49.99,
"currency": "USD",
"items": [{ "name": "Pro Plan", "quantity": 1 }]
}
}'
PayPal Purchase
curl -X POST https://api.tracelog.io/webhooks/events \
-H "Content-Type: application/json" \
-H "x-api-key: your-project-api-key" \
-d '{
"event_id": "PAY-1AB23456CD789012E",
"event_name": "purchase",
"timestamp": "2026-03-15T11:00:00Z",
"customer_id": "paypal-buyer@example.com",
"metadata": {
"amount": 29.99,
"currency": "EUR",
"gateway": "paypal"
}
}'
User Signup
curl -X POST https://api.tracelog.io/webhooks/events \
-H "Content-Type: application/json" \
-H "x-api-key: your-project-api-key" \
-d '{
"event_id": "signup-usr-456",
"event_name": "signup",
"timestamp": "2026-03-15T09:00:00Z",
"customer_id": "usr-456",
"session_id": "tl_sess_def789",
"metadata": {
"plan": "free",
"source": "organic"
}
}'
Server Event Without Session (with attribution)
When you don't have a browser session ID, you can provide referrer and utm fields for attribution:
curl -X POST https://api.tracelog.io/webhooks/events \
-H "Content-Type: application/json" \
-H "x-api-key: your-project-api-key" \
-d '{
"event_id": "sub-renewal-789",
"event_name": "subscription_renewal",
"timestamp": "2026-03-15T12:00:00Z",
"customer_id": "usr-789",
"referrer": "email-campaign",
"utm": {
"source": "email",
"medium": "newsletter",
"campaign": "march-renewal"
},
"metadata": {
"plan": "pro",
"amount": 19.99,
"currency": "USD"
}
}'
Refunds
TraceLog does not have a separate refund event type today. To make refunds subtract correctly from your revenue, AOV, and revenue-by-channel metrics, send them through this endpoint using your configured purchase event name with a negative amount.
Recipe
- Use the same
event_nameyou have configured for purchases in your project's revenue tracking (typicallypurchase). Do not invent a separaterefundevent name — it would appear in the events catalog but would not subtract from revenue. - Set the revenue field (
amount, or whatever field you configured in revenue tracking) to a negative value. - Use the refund ID from your payment processor as
event_idso retries are safely idempotent. - Optional but recommended: add
type: "refund"tometadataso you can filter refunds vs sales in the Events explorer. - If you stored the
session_idof the original purchase, include it to preserve campaign attribution on the refund.
Example
curl -X POST https://api.tracelog.io/webhooks/events \
-H "Content-Type: application/json" \
-H "x-api-key: your-project-api-key" \
-d '{
"event_id": "re_3abc123",
"event_name": "purchase",
"timestamp": "2026-03-15T10:30:00Z",
"customer_id": "cus_xyz",
"session_id": "tl_sess_abc123",
"metadata": {
"amount": -49.99,
"currency": "EUR",
"type": "refund",
"reason": "customer_request",
"original_order_id": "ORD-789"
}
}'
For partial refunds, use the partial amount in negative. For full refunds, mirror the original amount exactly.
Behavior
Idempotency
Duplicate event_id values are silently ignored and return { "processed": false }. Use natural identifiers from your payment processor or system (e.g., Stripe payment intent ID, order number) as event IDs to guarantee safe retries.
Session Linking
When session_id is provided, TraceLog links the event to the corresponding browser session and automatically inherits the session's UTM parameters and referrer. This means the event carries the same campaign attribution as the browsing session that originated the visit — no need to pass utm or referrer in the request.
If the session has no UTM data, the referrer and utm fields from the request body are used as a fallback.
To get the session ID on the client side, call tracelog.getSessionId() and pass it to your backend during the checkout or signup flow. This is what links server-side conversions to browsing sessions and enables campaign revenue attribution in your dashboard.
Without Session
When no session_id is provided, TraceLog creates a synthetic single-event session. If referrer and/or utm fields are included, they are stored for attribution on that synthetic session.
Events without a session ID still appear in your analytics and can be classified as conversions in the Event Catalog. The only difference is that they won't be connected to a browsing session, so they won't appear in session-based metrics like conversion rate by traffic source.
Rate Limits
The endpoint allows 100 requests per 60 seconds per API key.
If you exceed the rate limit, requests will return 429 Too Many Requests. For high-volume backends, implement a queue or batch processing to stay within the limit.