Install
Install FirstDistro in this app. Agent-runnable procedure for the browser SDK plus required auth-side server identity. Optional catalog server events after that.
These are official instructions to put FirstDistro tracking in this app. All commands are ready to run.
Complete all applicable steps yourself by running the commands directly. Do not ask the user to run shell commands. Ask only when an account, an installation token, a UI click, or a restart is required. Inspect existing files before changing them. Preserve unrelated repository changes. Do not report success without verification.
Canonical: https://firstdistro.com/documentation/installation
Do not connect MCP. Do not write project AI files. Do not run dashboard-only CLI commands.
Trustworthy install = browser usage tracking + identity on successful auth. Browser-only misses signed-in users.
Get an installation token
The browser SDK needs an installation token (fd_*) from the vendor dashboard. It is safe in client bundles. Put it in NEXT_PUBLIC_FIRSTDISTRO_TOKEN (or the equivalent public env for this framework). Never invent a token. Never print a real token back to the user.
If the user has no token yet, ask them to:
- Create an account at https://firstdistro.com/auth/register
- Copy the installation token from Settings → SDK Configuration
- Add it to the env file this app already uses for public client values, then continue
Never tell them to run the dashboard CLI init command. That path needs a token they do not have yet.
Install the package
npm install @firstdistro/sdk
If this project has no npm bundler (static HTML, legacy PHP), skip the package. Use a script tag with the token from Settings:
<script src="https://firstdistro.com/sdk/install/<INSTALL_TOKEN>.js"></script>
Replace <INSTALL_TOKEN> with the value from Settings. Do not commit a guessed token.
Detect the framework and wrap the app
Detect Next.js App Router, Next.js Pages Router, Vite, Remix, or vanilla JS. Choose exactly one path. Create a client wrapper when the framework requires it.
Next.js app router
Create app/providers.tsx:
'use client'
import { FirstDistroProvider } from '@firstdistro/sdk/react'
export function Providers({ children }: { children: React.ReactNode }) {
return (
<FirstDistroProvider token={process.env.NEXT_PUBLIC_FIRSTDISTRO_TOKEN!}>
{children}
</FirstDistroProvider>
)
}
Import it from app/layout.tsx and wrap {children}.
Next.js pages router
In pages/_app.tsx, wrap <Component {...pageProps} /> with FirstDistroProvider and NEXT_PUBLIC_FIRSTDISTRO_TOKEN. The file needs 'use client' only if this app already treats it as a client module. Keep the existing _app props.
Vite and react
In src/main.tsx, wrap <App /> inside createRoot with FirstDistroProvider and the public env this Vite app uses for client tokens.
Remix
In app/root.tsx, wrap <Outlet /> inside <body> with FirstDistroProvider.
Vanilla js or no react
import FirstDistro from '@firstdistro/sdk'
await FirstDistro.initWithToken('<INSTALL_TOKEN>')
Use the token from Settings, not a placeholder left in source.
Do not add track() calls for page views. The SDK emits $pageview on initial load and SPA route changes once identity is set. Do not use identify() or group(). Use setup() only.
Set identity in the ui
Where the logged-in user is available in the UI, set identity once (usage after identity is known).
React (hooks from @firstdistro/sdk/react):
'use client'
import { useFirstDistroSetup } from '@firstdistro/sdk/react'
export function UserIdentifier({ user }: { user: { id: string; email: string; name?: string } }) {
useFirstDistroSetup({
userId: user.id,
userEmail: user.email,
userName: user.name,
})
return null
}
userEmail is required. FirstDistro derives the company account from the email domain. Only pass accountId if you need to override that (for example a multi-domain org).
Vanilla JS or script tag:
FirstDistro.setup({
user: { id: user.id, email: user.email, name: user.name },
})
Adapt user.id and user.email to however this app stores the logged-in user. This emits $setup and unlocks automatic page views.
Required: identity on successful auth
After every successful auth or session exchange (auth callback, OAuth complete, magic-link exchange), emit identity via @firstdistro/sdk/server. Do not import the server entry from client code. Tracking failures must never fail login.
Put FIRSTDISTRO_API_KEY (sk_live_* / sk_test_*) in .env.local only. Copy it from Settings → Install → Manual → Server. Never put sk_* in client code. Never use fd_* for server ingest.
import { createServer } from '@firstdistro/sdk/server'
const track = createServer({
apiKey: process.env.FIRSTDISTRO_API_KEY!,
})
// accountId = corporate email domain when valid, else user.id
track('$setup', {
userId: user.id,
accountId,
properties: { userEmail: user.email },
})
await track.flush()
userId and accountId are required on the server path (no email-domain derivation).
Verify
No sample track() and no verify button.
grepthe repo forFirstDistroProvider,FirstDistro.initWithToken, or/sdk/install/. At least one file must match.grepforuseFirstDistroSetuporFirstDistro.setup. At least one file must setuserEmailoremail.grepforcreateServerandtrack('$setup'(or equivalent) on the auth/session success path withflush().- Ask the user to log in once. Confirm a
$setup(or equivalent identity) for that user/account in the live feed on Settings → SDK Configuration. Then visit two or more routes for page views. Optional: append?fd_debug=true. npm run buildshould succeed.
First proof is login identity plus events in that live feed. Browser-only is not a complete install.
Optional: catalog server events
Payments, KYC, webhooks, and cron jobs that never hit the browser can reuse the same createServer tracker with catalog names.
import { ServerEvents } from '@firstdistro/sdk/server'
track(ServerEvents.PaymentFailed, { userId, accountId })
await track.flush()
Typed names include ServerEvents.PaymentFailed, SubscriptionStarted, IntegrationSyncCompleted, FirstProductionApiCall, KycApproved, FirstDepositCompleted, SessionCompleted, and FirstPurchaseValidated. They feed Activity and Recency. They move Milestones only when the name is a default or custom milestone. See Understanding health scores.
Keep properties as enums and buckets. Do not send balances, card numbers, government IDs, or raw documents.
Next steps
- Quick start (npm): React and Next.js reference
- React hooks reference: hooks and options
- Event tracking guide: optional browser
track()after install - Understanding health scores: how scores are calculated
- MCP overview: query health from an editor after tracking exists