UserTrack
Developers / Integrations / Better Auth
Official plugin

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.

Overview

Why use the plugin

Native · verified

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 credentials shared

No database URL, no admin key. The plugin answers from inside your app using the adapter you already configured (Prisma, Drizzle, Kysely, Mongo, Convex…).

Nothing breaks

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).

Manual setup

Install and configure

  1. 1 · Create the integration in UserTrack

    Project → Integrations → Users → Better Auth. Enter your Better Auth base URL (baseURL + basePath, usually /api/auth). UserTrack generates USERTRACK_PROJECT_ID and a USERTRACK_SECRET (ut_int_…, shown once — rotate it if lost).

  2. 2 · Install the package
    npm install @usertrack/better-auth@latest
  3. 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. 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. 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.

Protocol

How verification works

Pull — source of truth

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.

Push — freshness (optional)

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.

Data & privacy

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.

AI setup

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:

  1. 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.
  2. 02Add the import { userTrack } from "@usertrack/better-auth" next to the existing imports.
  3. 03If a plugins array exists, append userTrack({ ... }) to it; never replace, reorder or remove existing plugins. If none exists, add plugins: [userTrack({ ... })].
  4. 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.
  5. 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.
  6. 06Preserve every existing Better Auth option: baseURL, database adapter, emailAndPassword, socialProviders, session settings, hooks and trustedOrigins stay exactly as they were.
  7. 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.
  8. 08Run the project's typecheck and tests after the change; the plugin ships its own types.
  9. 09Deploy. UserTrack can only verify once the new build with the env vars is live.
Troubleshooting

Common errors

Message
Cause
Fix
No UserTrack plugin found at …/usertrack/metrics (404)
The plugin is not installed, not in the plugins array, or the deploy is not live yet.
Install, register userTrack(), redeploy, verify again.
… rejected the signature (401)
USERTRACK_SECRET or USERTRACK_PROJECT_ID in the deployed environment differ from the integration.
Fix the env vars in every environment and redeploy — or rotate the secret in UserTrack and update the app.
… rejected the request as stale
The server clock is off by more than 5 minutes.
Fix NTP on the host; UserTrack retries automatically.
Could not reach …
Wrong base URL, app down or blocked.
The URL must be baseURL + basePath of Better Auth (usually /api/auth).
… could not count users (500)
The Better Auth database adapter failed to count the user model.
Check the adapter logs; adapter.count on user must work.
… is outdated / unsupported protocol version
Old plugin version.
Update @usertrack/better-auth.

Package source, tests and changelog: github.com/The-CodeCave/UserTrack · npm: @usertrack/better-auth · MIT.