Better Auth → UserTrack in two minutes.
@usertrack/better-auth adds one signed, read-only endpoint to your Better Auth instance. UserTrack pulls aggregate user counts from it every 4 hours — verified, without emails, names or any user record leaving your app.
Not on Better Auth? The same native protocol works for Prisma, Drizzle, Convex, Auth.js and custom apps with @usertrack/node.
Why use the plugin
The response is signed with a secret only your deployment holds, so UserTrack marks the source Verified regardless of hostname. Better Auth feeds the Signed up stage of your funnel.
No database URL, no admin key. The plugin answers from inside your app using the adapter you already configured (Prisma, Drizzle, Kysely, Mongo, Convex…).
Observational only: login, signup, sessions and your other plugins are untouched. If UserTrack is down, nothing in your auth flow notices.
Supported: better-auth ≥ 1.3.0 · ESM · Node 18.17+ and edge runtimes with WebCrypto · any database adapter that implements count (others fall back to a bounded id-only scan).
Install and configure
- 1 · Create the integration in UserTrack
Project → Integrations → Users → Better Auth. Enter your Better Auth base URL (baseURL + basePath, usually
/api/auth). UserTrack generatesUSERTRACK_PROJECT_IDand aUSERTRACK_SECRET(ut_int_…, shown once — rotate it if lost). - 2 · Install the package
npm install @usertrack/better-auth@latest
- 3 · Add the plugin to your Better Auth config
import { betterAuth } from "better-auth"; import { userTrack } from "@usertrack/better-auth"; export const auth = betterAuth({ // ...your existing options stay unchanged plugins: [ // ...your existing plugins stay unchanged userTrack({ projectId: process.env.USERTRACK_PROJECT_ID!, secret: process.env.USERTRACK_SECRET!, }), ], }); - 4 · Environment variables
# UserTrack (https://usertrack.dev) — registered-user growth metrics, no PII USERTRACK_PROJECT_ID= USERTRACK_SECRET=
Set the real values in your local env file and in your hosting provider for every environment. Never commit the secret.
- 5 · Deploy, then click Verify
UserTrack calls your app once, checks the signature, reads the total user count, records the first verified snapshot and schedules syncs every 4 hours. Result: Connected · 12,481 users detected · Verified via Better Auth.
How verification works
UserTrack sends POST <basePath>/usertrack/metrics with { protocolVersion: 1 } (plus days for the 30-day history on first sync, or from / to for any window). Each request carries x-usertrack-project, -timestamp, -nonce and an HMAC-SHA256 -signature over method, path, timestamp, nonce and body hash. The plugin verifies with a timing-safe comparison, rejects timestamps outside ±5 minutes and replayed nonces, then signs its response bound to the request nonce. UserTrack verifies that signature before storing anything.
On user.created / user.deleted the plugin posts a signed event to https://usertrack.dev/api/integrations/native/events — after the database write, fire-and-forget, 3-second timeout, deduplicated by event id. The dashboard shows signups between syncs; the next pull always wins. Disable with events: false.
{
"protocolVersion": 1,
"clientVersion": "0.2.0",
"source": "better-auth",
"projectId": "<USERTRACK_PROJECT_ID>",
"generatedAt": "2026-09-03T08:00:00.000Z",
"users": { "totalUsers": 12481, "newUsers": { "24h": 84, "7d": 491, "30d": 1832 }, "daily": [{ "day": "2026-08-04", "newUsers": 51 }, "…"] },
"capabilities": { "exactCounts": true, "history": true, "anonymousExcluded": false, "roles": ["users"] }
}Compatibility: UserTrack checks protocolVersion (supported: 1) and clientVersion (minimum 0.1.0; 0.1.x plugins that still send the users-only shape keep working) on every sync and shows an actionable error when the plugin is outdated.
What leaves your app
- ›Total registered users and new users in 24h / 7d / 30d, plus a daily series on first sync (aggregate counts only).
- ›Optionally activated users and converted / trial users from the same handler (counts only — never amounts, prices or MRR).
- ›Optional lifecycle events user.created / user.deleted / user.activated / trial.started / user.converted with a pseudonymous HMAC-derived subject — never the user id, email, name or profile.
- ›Never: emails, names, passwords, password hashes, session tokens, verification tokens, account or provider metadata.
Users created by the Better Auth anonymous plugin are excluded from registered-user counts automatically. On your public UserTrack page the source appears only as provenance: Verified via Better Auth. Trust semantics are the same as for every provider: UserTrack independently retrieves aggregate metrics from your installation; it does not claim that every account is a real human.
Let your coding agent do it
Connect the UserTrack MCP server (https://usertrack.dev/mcp, token from your developer page) to Claude Code, Cursor, Codex or VS Code and send:
Add this Better Auth project to UserTrack. Create or find the UserTrack project, create the native integration (usertrack_create_integration { provider: "native", source: "better-auth" }), install @usertrack/better-auth with this repo's package manager, add userTrack({ projectId: process.env.USERTRACK_PROJECT_ID!, secret: process.env.USERTRACK_SECRET! }) to the existing Better Auth plugins array without changing any other auth option, add both env vars to .env.example and the local env (never commit the secret), typecheck, deploy, verify the integration, trigger the first sync, publish the project (usertrack_update_project { isPublic: true } — a draft is not listed and its public URL 404s) and return the public UserTrack URL.The agent calls usertrack_create_integration (receives the secret once), usertrack_get_native_setup with source: "better-auth" (install command for npm / pnpm / yarn / bun, code change, env vars) and usertrack_verify_integration. The instructions it receives include these safety rules:
- 01Locate the file that calls betterAuth({ ... }) (commonly lib/auth.ts, src/lib/auth.ts, auth.ts, server/auth.ts, or convex/auth.ts). Do not create a second Better Auth instance.
- 02Add the import { userTrack } from "@usertrack/better-auth" next to the existing imports.
- 03If a plugins array exists, append userTrack({ ... }) to it; never replace, reorder or remove existing plugins. If none exists, add plugins: [userTrack({ ... })].
- 04Read projectId and secret from process.env.USERTRACK_PROJECT_ID / process.env.USERTRACK_SECRET (or the framework's env helper). Never hardcode the secret and never commit it.
- 05Add both variable names to .env.example (values empty) and the real values to the local env file that is git-ignored (.env.local / .env). Add them to the hosting provider's environment for every environment that serves the app.
- 06Preserve every existing Better Auth option: baseURL, database adapter, emailAndPassword, socialProviders, session settings, hooks and trustedOrigins stay exactly as they were.
- 07Do not change login, signup or session behaviour. The plugin is observational: it adds one signed endpoint (POST <basePath>/usertrack/metrics) and fire-and-forget lifecycle events.
- 08Run the project's typecheck and tests after the change; the plugin ships its own types.
- 09Deploy. UserTrack can only verify once the new build with the env vars is live.
Common errors
Package source, tests and changelog: github.com/The-CodeCave/UserTrack · npm: @usertrack/better-auth · MIT.