SDK Integration Guide
This guide covers the SDK integration path for browser apps, React, Next.js, Angular, Android, and Node.js.
Packages
For browser, React, Next.js, Angular, and Node.js integrations, install the npm package:
npm install @drivemetadata-ai/sdk
For Android integrations, install the Gradle dependency:
dependencies {
implementation 'com.drivemetadata:dmd-android-sdk:1.3.7'
}
Runtime entry points:
| Runtime | Import path | Use for |
|---|---|---|
| Browser | @drivemetadata-ai/sdk/browser | Plain JavaScript and browser applications |
| React | @drivemetadata-ai/sdk/react | React provider and hooks |
| Next.js | @drivemetadata-ai/sdk/next | Client-safe React helpers plus route tracking hooks |
| Angular | @drivemetadata-ai/sdk/angular | Angular provider and injectable service |
| Android | com.drivemetadata:dmd-android-sdk | Android client events, deep links, device tokens, uninstall support, and mobile ad metadata |
| iOS | DriveMetaDataiOSSDK | Native iOS events, IDFA permission, and universal links |
| React Native | react-native-drivemetadata | Cross-platform mobile SDK initialization, events, and deep-link data |
| Node.js | @drivemetadata-ai/sdk/node | Backend/server events only |
Do not import the Node entry point from browser bundles. Do not initialize the browser SDK during server rendering.
Required Keys
Browser and framework integrations use public browser tokens:
const browserConfig = {
clientId: 'client_xxx',
workspaceId: 'workspace_xxx',
appId: 'app_xxx',
token: 'public_token'
};
Use camelCase config keys in npm integrations. The backend payload still sends the backend-required metaData shape, but SDK configuration should use clientId, workspaceId, appId, apiHost, capturePageview, capturePageleave, schemaValidation, and beforeSend.
Node integrations use a server token stored only in backend environment variables:
DMD_SERVER_TOKEN=server_write_key
Android, iOS, and React Native integrations use mobile client credentials provisioned for the mobile source. Do not ship a backend/server token in a mobile app.
Environment Variables
For React/Vite-style browser builds, expose only public browser values:
VITE_DMD_CLIENT_ID=client_xxx
VITE_DMD_WORKSPACE_ID=workspace_xxx
VITE_DMD_APP_ID=app_xxx
VITE_DMD_TOKEN=public_token
For Next.js browser integrations, use NEXT_PUBLIC_ only for public browser values:
NEXT_PUBLIC_DMD_CLIENT_ID=client_xxx
NEXT_PUBLIC_DMD_WORKSPACE_ID=workspace_xxx
NEXT_PUBLIC_DMD_APP_ID=app_xxx
NEXT_PUBLIC_DMD_TOKEN=public_token
For Node/server integrations, do not use NEXT_PUBLIC_:
DMD_SERVER_TOKEN=server_write_key
Common Browser Config
const config = {
clientId: 'client_xxx',
workspaceId: 'workspace_xxx',
appId: 'app_xxx',
token: 'public_token',
apiHost: 'https://sdk.drivemetadata.com/v2',
consent: { analytics: 'pending', advertising: 'denied' },
capturePageview: true,
capturePageleave: true,
schemaValidation: 'warn',
delivery: {
retryDelayMs: 1000,
maxRetryDelayMs: 30000,
maxQueueSize: 100,
useBeacon: true
}
};
Recommended defaults:
| Option | Recommended | Notes |
|---|---|---|
consent.analytics | pending | Grant after consent manager resolves |
capturePageview | true | Sends automatic page views in browser runtimes |
capturePageleave | true | Sends lifecycle/page leave events |
schemaValidation | warn | Use strict in controlled integrations after testing |
delivery.useBeacon | true | Helps lifecycle delivery during navigation |
Consent First Setup
Enterprise integrations should start with analytics consent as pending or denied, then grant it after the consent manager resolves.
consent: {
analytics: 'pending',
advertising: 'denied',
personalization: 'denied',
functional: 'granted',
saleOfData: 'denied'
}
If consent is omitted, analytics defaults to pending and events are dropped until consent is granted.
Browser Integration
import {
consent,
flush,
getDmdHealth,
identify,
initDmdSDK,
page,
track
} from '@drivemetadata-ai/sdk/browser';
const dmd = initDmdSDK({
clientId: 'client_xxx',
workspaceId: 'workspace_xxx',
appId: 'app_xxx',
token: 'public_token',
consent: {
analytics: 'pending',
advertising: 'denied',
personalization: 'denied',
functional: 'granted',
saleOfData: 'denied'
}
});
consent.update({ analytics: 'granted' });
identify('user_123', { plan: 'enterprise' });
page('Product Page');
track('Product Viewed', { productId: 'sku_123' });
await flush();
console.log(getDmdHealth());
Browser API methods:
| Method | Use for |
|---|---|
track(event, properties, options) | custom events |
page(name, properties, options) | page views |
identify(userId, traits, options) | known users |
group(groupId, traits, options) | accounts/workspaces/organizations |
alias(previousId, userId, options) | identity merge |
flush() | force queued delivery |
reset() | reset identity/session state |
setConsent(consent) / consent.update(consent) | consent updates |
getDmdHealth() | diagnostics |
Backend Timestamp Fields
Every collector request sends these timestamp fields inside metaData:
| Field | Format | Notes |
|---|---|---|
timestamp | YYYY-MM-DD HH:mm:ss | UTC, no milliseconds, no timezone suffix |
requestSentAt | YYYY-MM-DD HH:mm:ss | UTC, no milliseconds, no timezone suffix |
requestReceivedAt | YYYY-MM-DD HH:mm:ss | UTC, no milliseconds, no timezone suffix |
If a caller provides an invalid timestamp string, the SDK normalizes it to the current UTC time instead of sending the invalid value to the backend.
Default Page Context
Browser, React, Next.js, and Angular integrations automatically add browser page context to each collector payload.
{
"metaData": {
"page": {
"url": "https://example.com/products/sku-123?utm_source=paid",
"path": "/products/sku-123",
"search": "?utm_source=paid",
"title": "Product Detail",
"referrer": "https://google.com/search?q=%5BREDACTED%5D"
}
}
}
The SDK reads this from window.location, document.title, and document.referrer. Query strings in search and referrer are redacted by default except common attribution parameters such as utm_source, utm_medium, utm_campaign, utm_term, and utm_content.
Node/server integrations do not automatically capture page context. Pass page details explicitly in server event properties if needed.
Default Identity, Session, Attribution
Browser, React, Next.js, and Angular integrations also add these fields by default:
| Field | Behavior |
|---|---|
requestId | generated UUID for each event |
anonymousId | persisted anonymous browser visitor ID |
sessionId | persisted browser session ID with idle timeout |
attributionData | stored click IDs and _fbc/_fbp when available |
utmParameter | stored UTM values when available |
The SDK stores anonymous/session/attribution state using the configured browser persistence mode and falls back safely when browser storage is blocked.
React Integration
Wrap the app once:
import { DmdProvider } from '@drivemetadata-ai/sdk/react';
export function App() {
return (
<DmdProvider
config={{
clientId: 'client_xxx',
workspaceId: 'workspace_xxx',
appId: 'app_xxx',
token: 'public_token',
consent: { analytics: 'pending', advertising: 'denied' }
}}
>
<Routes />
</DmdProvider>
);
}
For Vite React apps, the provider config commonly comes from import.meta.env:
const config = {
clientId: import.meta.env.VITE_DMD_CLIENT_ID,
workspaceId: import.meta.env.VITE_DMD_WORKSPACE_ID,
appId: import.meta.env.VITE_DMD_APP_ID,
token: import.meta.env.VITE_DMD_TOKEN,
consent: { analytics: 'pending' as const, advertising: 'denied' as const }
};
Track from components:
import { useDmdConsent, useDmdFlush, useIdentify, useTrackEvent } from '@drivemetadata-ai/sdk/react';
export function CheckoutButton() {
const track = useTrackEvent();
const identify = useIdentify();
const flush = useDmdFlush();
const consent = useDmdConsent();
async function onClick() {
consent({ analytics: 'granted' });
identify('user_123', { plan: 'enterprise' });
track('Checkout Started', { source: 'cart' });
await flush();
}
return <button onClick={onClick}>Checkout</button>;
}
Next.js App Router
Create a client provider:
'use client';
import { DmdProvider, useDmdAppRouterPageTracking } from '@drivemetadata-ai/sdk/next';
import { usePathname, useSearchParams } from 'next/navigation';
import type { ReactNode } from 'react';
function RouteTracking() {
const pathname = usePathname();
const searchParams = useSearchParams();
useDmdAppRouterPageTracking(pathname, searchParams.toString());
return null;
}
export function AnalyticsProvider({ children }: { children: ReactNode }) {
return (
<DmdProvider
config={{
clientId: process.env.NEXT_PUBLIC_DMD_CLIENT_ID!,
workspaceId: process.env.NEXT_PUBLIC_DMD_WORKSPACE_ID!,
appId: process.env.NEXT_PUBLIC_DMD_APP_ID!,
token: process.env.NEXT_PUBLIC_DMD_TOKEN!,
consent: { analytics: 'pending', advertising: 'denied' }
}}
>
<RouteTracking />
{children}
</DmdProvider>
);
}
Use it from app/layout.tsx:
import { AnalyticsProvider } from './AnalyticsProvider';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<AnalyticsProvider>{children}</AnalyticsProvider>
</body>
</html>
);
}
Next.js Pages Router
import type { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import { DmdProvider, useDmdPagesRouterPageTracking } from '@drivemetadata-ai/sdk/next';
const config = {
clientId: process.env.NEXT_PUBLIC_DMD_CLIENT_ID!,
workspaceId: process.env.NEXT_PUBLIC_DMD_WORKSPACE_ID!,
appId: process.env.NEXT_PUBLIC_DMD_APP_ID!,
token: process.env.NEXT_PUBLIC_DMD_TOKEN!,
consent: { analytics: 'pending' as const, advertising: 'denied' as const }
};
function PageTracking() {
const router = useRouter();
useDmdPagesRouterPageTracking(router);
return null;
}
export default function App({ Component, pageProps }: AppProps) {
return (
<DmdProvider config={config}>
<PageTracking />
<Component {...pageProps} />
</DmdProvider>
);
}
Angular Integration
Register the provider:
import { ApplicationConfig } from '@angular/core';
import { provideDmdAnalytics } from '@drivemetadata-ai/sdk/angular';
export const appConfig: ApplicationConfig = {
providers: [
provideDmdAnalytics({
clientId: 'client_xxx',
workspaceId: 'workspace_xxx',
appId: 'app_xxx',
token: 'public_token',
consent: { analytics: 'pending', advertising: 'denied' }
})
]
};
For Angular environment files, keep only the public browser token in frontend config:
export const environment = {
dmd: {
clientId: 'client_xxx',
workspaceId: 'workspace_xxx',
appId: 'app_xxx',
token: 'public_token'
}
};
Initialize and track:
import { isPlatformBrowser } from '@angular/common';
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
import { DmdAnalyticsService } from '@drivemetadata-ai/sdk/angular';
@Component({
selector: 'app-root',
template: '<router-outlet />'
})
export class AppComponent implements OnInit {
constructor(
private readonly dmd: DmdAnalyticsService,
@Inject(PLATFORM_ID) private readonly platformId: object
) {}
ngOnInit() {
if (!isPlatformBrowser(this.platformId)) return;
this.dmd.init();
this.dmd.setConsent({ analytics: 'granted' });
this.dmd.track('App Loaded');
}
}
For Angular Universal, call this.dmd.init() only when isPlatformBrowser(platformId) is true.
Node Server Integration
import { createDmdServerClient } from '@drivemetadata-ai/sdk/node';
const dmd = createDmdServerClient({
clientId: 'client_xxx',
workspaceId: 'workspace_xxx',
appId: 'app_xxx',
token: process.env.DMD_SERVER_TOKEN!,
timeoutMs: 5000,
retry: {
attempts: 3,
minDelayMs: 250,
maxDelayMs: 2000
}
});
await dmd.track({
userId: 'user_123',
event: 'Order Completed',
messageId: 'evt_123',
idempotencyKey: 'order_123',
properties: {
amount: 500,
currency: 'USD'
}
});
Use messageId for a unique event ID and idempotencyKey for business events that may retry, such as orders, invoices, subscriptions, and webhooks.
Server-side page context is explicit:
await dmd.page({
userId: 'user_123',
properties: {
page: {
url: 'https://example.com/orders/123',
path: '/orders/123',
title: 'Order Detail'
}
}
});
Android Client Integration
Use the Android SDK for native mobile event collection, deep links, device token updates, optional mobile ad metadata, and Facebook install referrer support.
dependencies {
implementation 'com.drivemetadata:dmd-android-sdk:1.3.7'
}
Initialize the SDK once from your Application subclass:
import android.app.Application;
import com.drivemetadata.DriveMetaData;
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
DriveMetaData driveMetaData = new DriveMetaData.Builder(
this,
1234,
"android_client_token",
5678,
"meta_app_id"
).build();
DriveMetaData.setSingletonInstance(driveMetaData);
}
}
Send custom events with JSON properties:
JSONObject properties = new JSONObject();
properties.put("screen", "Checkout");
DriveMetaData.with(this).sendTags(
properties.toString(),
"Checkout Started"
);
For the full Android setup, including manifest permissions, callbacks, deep links, device tokens, ad tracking, debug logs, cleanup, and Meta App ID setup, see Android client integration.
iOS Native Integration
Use the iOS SDK for native app events, IDFA permission, and universal links.
pod 'DriveMetaDataiOSSDK'
import DriveMetaDataiOSSDK
DriveMetaData.initializeShared(
clientId: <DMD_CLIENT_ID>,
clientToken: "<DMD_CLIENT_TOKEN>",
clientAppId: <DMD_CLIENT_APP_ID>
)
For CocoaPods setup, ATT configuration, universal links, and Swift event examples, see iOS native integration.
React Native Integration
Use the React Native plugin for mobile SDK initialization, event metadata, and deep-link background data retrieval.
npm install react-native-drivemetadata
import {sdkInit, sendTags, getBackgroundData} from 'react-native-drivemetadata';
await sdkInit(1234, 'client_token', 5678);
await sendTags({userDetails: {first_name: 'Amit'}}, 'user_registration');
For the full setup, see React Native integration.
Diagnostics
Use health diagnostics when events do not appear:
import { flush, getDmdHealth } from '@drivemetadata-ai/sdk/browser';
console.log(getDmdHealth());
await flush();
Common drop reasons:
| Reason | Meaning | Fix |
|---|---|---|
consent_denied | Analytics consent is not granted | Grant consent after the consent manager resolves |
payload_too_large | Event exceeds configured payload size | Reduce event properties |
queue_limit_exceeded | Offline queue is full | Flush more often or increase queue size |
queue_ttl_expired | Event stayed offline past TTL | Increase TTL only if business requirements allow it |
REST API Ingestion
Use REST API ingestion only from trusted server-side systems. Do not call collector APIs directly from mobile or browser clients. See REST API ingestion.
Backend Payload IDs
The SDK sends requestId, anonymousId, and sessionId as UUID strings in metaData.
{
"metaData": {
"requestId": "019de75e-dd28-779b-8084-2b3091b6de32",
"anonymousId": "019c8fe4-1fc7-7898-8089-f951a8bbfefb",
"sessionId": "019de75e-db3f-75e2-8054-0167411aa4ba"
}
}
Production Checklist
- Use
@drivemetadata-ai/sdk/browser,/react,/next, or/angularonly for public browser tokens. - Use
com.drivemetadata:dmd-android-sdkonly with Android client credentials. - Use
DriveMetaDataiOSSDKandreact-native-drivemetadataonly with mobile client credentials. - Use
@drivemetadata-ai/sdk/nodeonly on the server with backend-only environment variables. - Start analytics consent as
pendingordenieduntil your consent manager grants it. - Call
flush()before important navigation or checkout completion boundaries when needed. - Check
getDmdHealth()during integration testing. - Verify SSR builds do not initialize browser analytics on the server.
More Guides
- Browser SDK: npm-browser-sdk.md
- React and Next.js: react-next-integration.md
- Android client: android-client-integration.md
- Android App Links and Dynamic Deep Links: android-deep-linking.md
- iOS native: ios-native-integration.md
- React Native: react-native-integration.md
- REST API ingestion: rest-api-ingestion.md
- Event types: event-types.md
- Partner integrations: partner-integrations.md
- Node server: node-server-integration.md