# Install

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/install.md

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:

1. Create an account at https://firstdistro.com/auth/register
2. Copy the installation token from [Settings → SDK Configuration](https://firstdistro.com/dashboard/settings/sdk-configuration)
3. 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

```bash
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:

```html
<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`:

```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

```ts
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`):

```tsx
'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:

```ts
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.

```ts
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.

- `grep` the repo for `FirstDistroProvider`, `FirstDistro.initWithToken`, or `/sdk/install/`. At least one file must match.
- `grep` for `useFirstDistroSetup` or `FirstDistro.setup`. At least one file must set `userEmail` or `email`.
- `grep` for `createServer` and `track('$setup'` (or equivalent) on the auth/session success path with `flush()`.
- 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](https://firstdistro.com/dashboard/settings/sdk-configuration). Then visit two or more routes for page views. Optional: append `?fd_debug=true`.
- `npm run build` should 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.

```ts
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](https://firstdistro.com/documentation/guides/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)](https://firstdistro.com/documentation/sdk/quick-start): React and Next.js reference
- [React hooks reference](https://firstdistro.com/documentation/sdk/react-hooks): hooks and options
- [Event tracking guide](https://firstdistro.com/documentation/guides/event-tracking): optional browser `track()` after install
- [Understanding health scores](https://firstdistro.com/documentation/guides/understanding-health-scores): how scores are calculated
- Human docs page: https://firstdistro.com/documentation/installation
