Connecting Users
Link anonymous visitors to logged-in users so streaks follow across devices.
Connecting Users (Alias)
By default Streakfox tracks a random visitor ID that lives in localStorage.
When that person signs in, generate a stable pseudonymous userHash on your server and link the browser visitor to it with the alias API. Never put an email address, database ID, or another raw account identifier in the widget markup.
Coupon and URL reward values are protected as well. To reveal one inside the widget, provide the matching short-lived alias token as identityToken; without that proof, Streakfox keeps the value out of public state and relies on the outbound reward webhook for delivery.
👉 You only need to alias each visitor once. Repeating the same visitor-to-user binding is idempotent. The binding is intentionally one-way: trying to attach that anonymous visitor to a different user returns 409 ALIAS_IDENTITY_CONFLICT and leaves its history with the original user.
1 Anonymous copy-paste integration
<!-- Embed the widget script anywhere in <body> -->
<script src="https://cdn.streakfox.com/widget.js" async data-project="<PROJECT_ID>"></script>For a signed-in page, render server-created values instead:
<script
src="https://cdn.streakfox.com/widget.js"
defer
data-project="<PROJECT_ID>"
data-user-hash="<SERVER_GENERATED_PSEUDONYMOUS_HASH>"
data-identity-token="<SHORT_LIVED_SIGNED_ALIAS_TOKEN>"
></script>The token payload must identify the same project and userHash, expire within ten minutes, and be signed with that project's alias signing secret.
2 React / Next.js (server-signed identity)
Create a server route that derives a pseudonymous hash from the authenticated account and signs the short-lived identity token. Keep both salts on the server:
// /app/api/streak/identity/route.ts
import { createHash, createHmac } from "node:crypto";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const session = await requireSession();
const { anonymousId } = await req.json();
const siteKey = process.env.NEXT_PUBLIC_STREAK_PROJECT_ID!;
const userHash = createHash("sha256")
.update(`${process.env.STREAK_HASH_SALT}:${session.user.id}`)
.digest("hex");
const now = Math.floor(Date.now() / 1000);
const payload = Buffer.from(
JSON.stringify({ siteKey, anonymousId, userHash, iat: now, exp: now + 600 }),
).toString("base64url");
const signature = createHmac("sha256", process.env.STREAK_ALIAS_SIGNING_SECRET!)
.update(payload)
.digest("base64url");
return NextResponse.json({ userHash, token: `${payload}.${signature}` });
}After sign-in, send the browser's sd_visitor_id to this route, then POST the returned token from the browser:
await fetch(`https://api.streakfox.com/v1/alias?siteKey=${encodeURIComponent(siteKey)}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});The query parameter lets the CORS preflight resolve that project's allowed origin; the signed token remains the authority for the mutation. Then render the hosted script with the returned userHash and token as shown above. The alias call performs the one-time history merge; refresh the short-lived token when the signed-in page is rendered again.
3 How it works under the hood
- Before login – the browser gets a random
visitorId(sd_visitor_id) and the server creates a record keyed by that id. - Alias call – your trusted server links that visitor ID to a pseudonymous
userHash. - After alias – requests using either identity resolve to the same visitor record, so the streak follows the account across devices.
Duplicate calls with the same userHash are safe. A different userHash is rejected so an exposed or reused browser visitor ID cannot transfer somebody's streak history to another account.
Troubleshooting
• CORS error? Make sure the domain of your site is added to Project → Settings → Allowed Origins in the Streakfox dashboard.
• visitorId missing? Some browsers block localStorage in third-party iframes. The SDK falls back to an in-memory id that lasts until the tab closes.
• 409 ALIAS_IDENTITY_CONFLICT? That browser visitor is already linked to another account. Keep the original binding; use a new anonymous visitor ID for a genuinely different person.
Need help? Join our Discord or email support@streakfox.com – we're happy to assist!