Signed-in users: send the same userId
When the person is signed in, the server SDK only needs your user id — the one the browser passes
to identify():
analytics.track('invoice_paid', { amount_bucket: '100-500' }, { userId: user.id });
Different ids on the two sides make one user into two people.
Visitors who have not signed in: forward anonymousId
Before sign-in there is no user id. Send the browser’s anonymous id with the request, and pass it on:
// browser
await fetch('/api/quote', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-togo-anonymous-id': analytics.getAnonymousId() ?? '',
'x-togo-session-id': analytics.getSessionId() ?? '',
},
body: JSON.stringify(form),
});
// server
const anonymousId = request.headers.get('x-togo-anonymous-id') || undefined;
const sessionId = request.headers.get('x-togo-session-id') || undefined;
if (anonymousId) {
analytics.track('quote_requested', { seats_bucket: '10-50' }, { anonymousId, sessionId });
}
The header names are yours to choose; Togo never reads them. The getters return undefined until
the browser’s first event, and while consent is pending or denied, so handle the empty case.
Never make an id up
An event with neither userId nor anonymousId is dropped with an error, because nobody could be
credited with it. Do not fill the gap with a random id, a request id or a constant: each would
create a person who does not exist. If the server cannot tell who it is about, skip the event or
send it from the browser instead.
Join the visit with sessionId
sessionId is optional. Forwarded from getSessionId(), it puts the server event in the same
session as the browser events around it. Without it, the event is still the right person’s, but
outside any session.
Before you return
In a serverless function, await analytics.flush() before returning the response, or the events
may never leave (Node.js and serverless).