Browse documentation

Server events

Record backend-confirmed actions and connect them to the originating visitor session.

Choose the right event

Checkout example
checkout_clicked
Record in the browser when the visitor clicks the checkout link.
begin_checkout
Record on your server after the payment provider creates a checkout session, before redirecting. This does not confirm that the hosted payment page loaded.
Payment successful
Verify the provider webhook, then send a purchase through the revenue endpoint.

These custom event names are conventions you choose. Tracwell does not create them automatically. Use a distinct event for each stage to avoid counting a browser click and backend result as the same action.

Authentication and identity

Open Settings → Website → Server key for an active Product-mode website. Use the same write-only server key used for revenue collection. Keep it in a server environment variable; public project keys and MCP read keys cannot authenticate this endpoint.

Send Authorization: Bearer YOUR_SERVER_KEY and Content-Type: application/json to POST https://collect.tracwell.app/v1/server/events. The key determines the project; do not include a project ID or public key in the body.

Read analytics.getSession() in the browser and pass its anonymousId and sessionId with your business request. Both are required. Preserve them in checkout metadata for later payment attribution. An optional opaque user_id does not replace them.

Handle consent and tracking preferences before forwarding identity. If there is no browser session, skip this attributed event rather than inventing one. Server authentication does not verify that your business action or payment succeeded.

Send a custom event

This example runs after your business action succeeds. Supply the originating page context, not the server URL or server device details. No browser SDK runs on the server.

Server request
// Server only. Pass the browser's anonymousId and sessionId
// with the business request, after handling consent on your site.
const batch = {
  batch_version: 1,
  batch_id: crypto.randomUUID(),
  sent_at: new Date().toISOString(),
  sdk: { name: "tracwell-server", version: "0.1.0" },
  events: [{
    schema_version: 1,
    event_id: crypto.randomUUID(),
    event_type: "event",
    event_name: "begin_checkout",
    timestamp: new Date().toISOString(),
    anonymous_id: anonymousId,
    session_id: sessionId,
    context: {
      url: "https://example.com/pricing",
      path: "/pricing"
    },
    properties: { plan: "pro", provider: "dodo" }
  }]
};

// Create this batch once per action. Retry this same serialized body
// and event_id; persist it if retries must survive a process restart.
const body = JSON.stringify(batch);
const response = await fetch(
  "https://collect.tracwell.app/v1/server/events",
  {
    method: "POST",
    headers: {
      authorization: `Bearer ${process.env.TRACWELL_SERVER_KEY}`,
      "content-type": "application/json"
    },
    body,
    signal: AbortSignal.timeout(5000)
  }
);
const result = await response.json();
if (response.status !== 202 || result.receipt_version !== 1 ||
    result.batch_id !== batch.batch_id ||
    result.accepted_events !== batch.events.length) {
  throw new Error("Tracwell collection was not acknowledged");
}
Request limits
Batch
Version 1; UUID batch ID; ISO timestamps with milliseconds; server SDK name and semantic version; 1–50 events; at most 48 KiB.
Event
UUID event ID; event_type must be event; required name, timestamp, anonymous_id, session_id and context; at most 16 KiB.
Context
Required absolute URL and slash-prefixed path. Title, referrer and the five standard UTM properties are optional.
Timestamps
At most 24 hours old or 5 minutes in the future, including sent_at.
Reserved
identify, page_view and revenue event names; properties beginning with $revenue_. Use the revenue endpoint for purchases and refunds.

Custom properties are optional and follow the custom event naming and property limits. Duplicate event IDs within one batch are rejected.

Acceptance and retries

202 Accepted
{
  "receipt_version": 1,
  "receipt_id": "0190f3c6-7a10-7cc2-8b48-8e1acb5f8c01",
  "batch_id": "0190f3c6-7a10-7cc2-8b48-8e1acb5f8b01",
  "accepted_events": 1,
  "accepted_at": "2026-09-05T12:00:00.000Z"
}

A matching receipt confirms Queue acceptance, not yet database persistence. Reuse the original event IDs, timestamps and payload on retries. Repeated delivery is deduplicated by the ingestion/reporting path.

Await the request before returning from a short-lived server function. Use bounded backoff for network failures or retryable responses. Durable retry across restarts requires storing the pending event in your application; an in-memory retry loop cannot provide that guarantee.

Decide how collection failures affect your product. For checkout, report an analytics failure separately and preserve a valid checkout redirect. Verify the event in Tracwell after ingestion before declaring the integration complete.
Errors
400
Invalid JSON, schema, timestamp, event size, or reserved revenue fields. Correct the payload.
401
Missing or unavailable server key, including a website without active Product-mode collection access.
405 / 415
Use POST with Content-Type: application/json.
413
Request exceeds the byte limit. Reduce the batch size.
429
Read error.retryable: retryable rate limits may be retried; an exhausted event allowance is not retryable.
500 / 503
Temporary collection failure. Retry the same batch with bounded backoff.