Install

Install FirstDistro for browser and server. One npm package, same track() verb. Browser token (fd_*) and server key (sk_*). First server event in under 3 minutes.

One package (@firstdistro/sdk), one track() verb, two surfaces: browser UI and server truth (payments, KYC, syncs, sessions). Goal: first server event in under 3 minutes.

Get credentials in Settings → SDK Configuration. Prefer the CLI for the fastest path: npx firstdistro init.

Keys

SurfaceCredentialEnv varExposure
BrowserInstall token fd_*NEXT_PUBLIC_FIRSTDISTRO_TOKENSafe to expose in client bundles
ServerAPI key sk_live_* / sk_test_*FIRSTDISTRO_API_KEYKeep secret. Server only

Warning: Never put sk_* in client code. Never use fd_* for server ingest.

Choose your surface

1. Install

bash
npm install @firstdistro/sdk

2. Init and identify

Wrap the app with FirstDistroProvider (NEXT_PUBLIC_FIRSTDISTRO_TOKEN), then call useFirstDistroSetup with user id and email when the user logs in. Account is derived from the email domain.

Full React and Next.js steps (provider, layout, privacy): Quick start (npm).

3. Track

Page views ($pageview) are automatic after setup(). Optional useTrack() calls record product moments. They count toward Activity and Recency. They only move Milestones when the event name is a default or custom milestone (see Understanding health scores).

4. Verify

Log in to your app and visit a couple of routes. Watch the live feed on Settings → SDK Configuration, or open Customer Insights once accounts appear. Append ?fd_debug=true locally for a debug badge.

Script tag: for static sites without a bundler, see the script-tag section on Quick start (npm).

These eight typed names cover the backend moments that matter most for customer health. Use the ServerEvents constants so typos fail at compile time.

ConstantEvent nameRole
ServerEvents.PaymentFailedpayment_failedRisk signal (failed charge)
ServerEvents.SubscriptionStartedsubscription_startedPaid plan begins
ServerEvents.IntegrationSyncCompletedintegration_sync_completedRecurring activity (sync finished)
ServerEvents.FirstProductionApiCallfirst_production_api_callFirst live API usage
ServerEvents.KycApprovedkyc_approvedOne-time adoption moment
ServerEvents.FirstDepositCompletedfirst_deposit_completedFirst funded deposit
ServerEvents.SessionCompletedsession_completedRecurring activity (aggregated session)
ServerEvents.FirstPurchaseValidatedfirst_purchase_validatedOne-time adoption moment

All of these update Activity and Recency like other custom events. They move the Milestones component only if you add them as custom milestones in event settings (or if the name already matches a default milestone). Defaults today are browser-oriented names such as account_created and first_feature_used. See Understanding health scores.

Raw api (curl)

Prefer @firstdistro/sdk/server in production. For a one-off test, call the batch endpoint directly with a server key.

Endpoint: POST https://firstdistro.com/api/tracking/events/batch
Auth: X-API-Key: sk_live_… (or sk_test_…)

Required per event: eventName, timestamp. For server keys, send userId and accountId. sessionId is optional when authenticating with sk_* (browser keys still require it). Omit vendorId; it is inferred from the key.

bash
curl -sS -X POST 'https://firstdistro.com/api/tracking/events/batch' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: sk_live_YOUR_KEY' \
  -d '{
    "batchId": "batch_test_001",
    "events": [
      {
        "eventName": "payment_failed",
        "userId": "user_123",
        "accountId": "acct_456",
        "timestamp": "2026-08-10T12:00:00.000Z",
        "properties": {
          "source": "server",
          "failure_reason_category": "card_declined"
        }
      }
    ]
  }'

Node equivalent:

typescript
await fetch('https://firstdistro.com/api/tracking/events/batch', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.FIRSTDISTRO_API_KEY!,
  },
  body: JSON.stringify({
    batchId: `batch_${Date.now()}`,
    events: [
      {
        eventName: 'payment_failed',
        userId: 'user_123',
        accountId: 'acct_456',
        timestamp: new Date().toISOString(),
        properties: {
          source: 'server',
          failure_reason_category: 'card_declined',
        },
      },
    ],
  }),
})

Pii guidance

Server events are the right channel for sensitive product signals. Keep properties minimal.

Never send: exact balances, card or account numbers, government IDs, full transaction amounts, raw KYC documents.

Send instead: enums and buckets, for example failure_reason_category: 'card_declined', verification_tier: 'standard', count_bucket: 'small'.

Treat every property as readable by support staff.

Health scores

Browser $pageview events power Activity, Engagement, and Recency out of the box. Recommended server events (and other custom track() calls) feed Activity and Recency the same way. The Milestones component only counts names in the default milestone list, names starting with milestone_, or custom milestones you configure. Details: Understanding health scores.

Next steps