# Server-side sessions and session hints

> How server-side events are grouped into visits with the daily salted visit key and how session_hint splits or joins visits.

Server-side events join visits exactly like browser hits.

## The visit key

The visit key is an HMAC of the **daily salt**, the site, the visitor's
IP and User-Agent (and your `session_hint`, if you send one, and the
hostname on sites that count each domain separately). The key is kept only
in Redis, for 30 minutes of inactivity and never past midnight UTC. The
salt is random, changes every UTC day, is kept only in Redis and expires
30 minutes after its day ends, so the key can't be recomputed later.

- Forward the **visitor's** IP and User-Agent, not your server's, or every
  visitor looks like one person.
- The first pageview or custom event starts a visit. The visit keeps the
  source, device and country of that first event.
- If you send the same IP and User-Agent from the browser tracker and from
  the server, server events join the visitor's browser visit: a purchase
  confirmed by a webhook is attributed to the campaign that brought the
  visitor. For that, the webhook needs the IP and User-Agent from the
  original checkout request. Store them with the order only for as long as
  the checkout takes, then delete them.

## `session_hint`

An optional opaque string, for example a cart id or a job id:

```json
{ "type": "event", "name": "Checkout step", "url": "https://example.com/checkout", "session_hint": "cart_8f2a…", "user_agent": "…" }
```

It's added to the HMAC input, so:

- events with the same hint (and the same IP/User-Agent) form one visit,
- events with different hints are separate visits, even from the same IP
  and User-Agent (for example, many users behind one office proxy, or a
  backend without the visitor's IP).

The hint is **never stored**: it's hashed together with the daily salt and
discarded. Still, don't use emails or user ids as hints. A random id per
cart or session is enough.

## Timestamps

A `timestamp` up to 72 hours old places the event at that time. Visits are
still keyed by the day the event happened (UTC), so a late event joins a
visit only if that visit is still open (its last event is less than 30
minutes before the event's time). Older back-fills create their own visits.
