> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tappd.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Tappd Mobile SDK Full API Reference — React Native

> Full React Native SDK reference covering identify, track, push notifications, in-app messages, banners, and utility methods with TypeScript interfaces.

The Tappd Mobile SDK exposes a small, focused surface of methods that cover every use case — from identifying users and tracking events to managing push tokens and displaying in-app messages. This page documents every method, its parameters, return types, and example usage.

## Constructor

### new TappdSDK(config)

Create a new SDK instance. You typically do this once — at your app root or inside a context provider — and share it throughout your application.

**Signature:** `new TappdSDK(config: TappdConfig)`

```typescript theme={null}
import TappdSDK from '@tappd/mobile-sdk';

const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  debug: __DEV__
});
```

See the [TypeScript Types](#typescript-types) section below for the full `TappdConfig` interface.

***

## Core Methods

### identify(attributes)

Identify or update a customer using one or more identifiers. When you call `identify()`, the SDK automatically merges any events or sessions that were tracked anonymously before identification. The SDK generates its own internal identifier; you never need to manage one yourself.

**Signature:** `tappd.identify(attributes: CustomerAttributes): Promise<void>`

**TypeScript interface:**

```typescript theme={null}
interface CustomerAttributes {
  external_id?: string;
  email?: string;
  phone?: string;
  alias?: { type: string; value: string; };
  name?: string;
  attributes?: { [key: string]: any; };
  [key: string]: any;
}
```

<Note>
  You must include at least one identifier — `external_id`, `email`, `phone`, or `alias` — in the `attributes` object. The call will fail if none are provided.
</Note>

**Identification methods:**

<Tabs>
  <Tab title="External ID (Recommended)">
    Use your internal database user ID as the primary identifier for best results.

    ```javascript theme={null}
    await tappd.identify({
      external_id: 'ext_123',
      name: 'John Doe',
      email: 'john@example.com',
      phone: '+1234567890',
      age: 28,
      plan: 'premium',
      company: 'Acme Corp'
    });
    ```
  </Tab>

  <Tab title="Email Only">
    The SDK automatically normalizes the email address (lowercased and trimmed).

    ```javascript theme={null}
    await tappd.identify({
      email: 'user@example.com',
      name: 'John Doe',
      plan: 'free'
    });
    ```
  </Tab>

  <Tab title="Phone Only">
    ```javascript theme={null}
    await tappd.identify({
      phone: '+1234567890',
      name: 'John Doe',
      plan: 'free'
    });
    ```
  </Tab>

  <Tab title="Alias">
    Use an alias when you have a custom or legacy identifier scheme.

    ```javascript theme={null}
    await tappd.identify({
      alias: {
        type: 'session_id', // e.g., 'session_id', 'legacy_user_id'
        value: '1234'
      },
      name: 'John Doe',
      plan: 'free'
    });
    ```
  </Tab>

  <Tab title="Multiple Identifiers">
    Providing multiple identifiers improves customer matching across sessions and devices.

    ```javascript theme={null}
    await tappd.identify({
      external_id: 'user_123',
      email: 'john@example.com',
      phone: '+1234567890',
      name: 'John Doe',
      attributes: {
        plan: 'premium',
        company: 'Acme Corp'
      }
    });
    ```
  </Tab>
</Tabs>

**Identifier behaviour:**

| Identifier    | Notes                                                                                                 |
| ------------- | ----------------------------------------------------------------------------------------------------- |
| `external_id` | Your database user ID — used as the primary identifier. Generates an internal `userId` automatically. |
| `email`       | Auto-normalized (lowercased and trimmed). Can be the sole identifier.                                 |
| `phone`       | Can be the sole identifier.                                                                           |
| `alias`       | Useful for legacy or custom identification schemes. Custom alias types are stored as `'custom'`.      |

**Lookup priority:** When the SDK searches for an existing customer it tries `external_id` → `email` → `phone` → `alias`.

**How identification works:**

<Steps>
  <Step title="Provide at least one identifier">
    Pass `external_id`, `email`, `phone`, or `alias` inside `attributes`.
  </Step>

  <Step title="Customer matching">
    The SDK finds an existing customer record or creates a new one.
  </Step>

  <Step title="Event association">
    All future `track()` calls are automatically associated with this user.
  </Step>

  <Step title="Session linking">
    All current and future sessions are linked to the identified user.
  </Step>

  <Step title="Anonymous data merge">
    Any events tracked before `identify()` are merged into the customer profile.
  </Step>
</Steps>

***

### track(eventName, properties?)

Track a custom event with optional properties. After you call `identify()`, the SDK automatically associates every subsequent `track()` call with the identified user — you don't need to repeat identifier fields. You can, however, pass identifiers explicitly in `properties` if you need to associate an event with a specific user without a prior `identify()` call.

**Signature:** `tappd.track(eventName: string, properties?: EventProperties): Promise<void>`

**EventProperties interface:**

```typescript theme={null}
interface EventProperties {
  userId?: string;        // Internal user ID from identify response
  user_id?: string;       // Alternative field name
  external_id?: string;   // External user identifier
  externalId?: string;    // Alternative camelCase field name
  email?: string;
  phone?: string;
  alias?: {
    type: string;
    value: string;
  };
  sessionId?: string;     // Link event to a specific session
  [key: string]: any;     // Any additional event properties
}
```

**Event tracking examples:**

<Tabs>
  <Tab title="After identify (auto-associated)">
    ```javascript theme={null}
    await tappd.identify({
      external_id: 'user_123',
      email: 'john@example.com'
    });

    // No identifier needed — SDK associates this automatically
    await tappd.track('purchase', {
      amount: 99.99,
      currency: 'USD',
      productId: 'prod_123'
    });
    ```
  </Tab>

  <Tab title="With external_id">
    ```javascript theme={null}
    await tappd.track('button_clicked', {
      external_id: 'user_123',
      button: 'checkout',
      screen: 'CartScreen'
    });
    ```
  </Tab>

  <Tab title="With email">
    ```javascript theme={null}
    await tappd.track('email_sent', {
      email: 'user@example.com',
      template: 'welcome_email',
      subject: 'Welcome to our platform'
    });
    ```
  </Tab>

  <Tab title="With phone">
    ```javascript theme={null}
    await tappd.track('sms_sent', {
      phone: '+1234567890',
      message: 'verification_code',
      carrier: 'att'
    });
    ```
  </Tab>

  <Tab title="With alias">
    ```javascript theme={null}
    await tappd.track('session_event', {
      alias: {
        type: 'session_id',
        value: '1234'
      },
      event: 'screen_view',
      screen: 'DashboardScreen'
    });
    ```
  </Tab>
</Tabs>

**Event lookup priority:** `userId` / `user_id` → `external_id` / `externalId` → `email` → `phone` → `alias`.

***

### trackScreen(screenName, properties?)

Track a screen view, equivalent to a page view in web analytics.

**Signature:** `tappd.trackScreen(screenName: string, properties?: EventProperties): Promise<void>`

```javascript theme={null}
await tappd.trackScreen('HomeScreen', {
  category: 'main',
  section: 'dashboard'
});
```

<Tip>
  Use `useFocusEffect` from React Navigation instead of `useEffect` to ensure screen views fire every time a screen comes into focus, not just on mount.
</Tip>

***

### setExternalId(externalId)

Set the external ID for the currently identified user.

**Signature:** `tappd.setExternalId(externalId: string): Promise<void>`

```javascript theme={null}
await tappd.setExternalId('ext_user_123');
```

<Warning>
  Throws an error if no user has been identified yet. Always call `identify()` first.
</Warning>

***

### setUserAttributes(attributes)

Update attributes for the currently identified user. Pass any fields from `CustomerAttributes` to overwrite existing values.

**Signature:** `tappd.setUserAttributes(attributes: CustomerAttributes): Promise<void>`

```javascript theme={null}
await tappd.setUserAttributes({
  plan: 'enterprise',
  lastLogin: new Date().toISOString(),
  status: 'active'
});
```

<Warning>
  Throws an error if no user has been identified yet.
</Warning>

***

### setCustomAttributes(attributes)

Set free-form custom attributes on the currently identified user.

**Signature:** `tappd.setCustomAttributes(attributes: Record<string, any>): Promise<void>`

```javascript theme={null}
await tappd.setCustomAttributes({
  preferences: {
    theme: 'dark',
    language: 'en'
  },
  customField: 'value'
});
```

<Warning>
  Throws an error if no user has been identified yet.
</Warning>

***

## Push Notification Methods

### registerPushToken(token, platform)

Register a Firebase Cloud Messaging (FCM) push token for the current user so they can receive push notifications.

**Signature:** `tappd.registerPushToken(token: string, platform: 'ios' | 'android'): Promise<void>`

```javascript theme={null}
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';

const token = await messaging().getToken();
await tappd.registerPushToken(
  token,
  Platform.OS === 'ios' ? 'ios' : 'android'
);
```

<Warning>
  Throws an error if the user is not identified. Call `identify()` before `registerPushToken()`.
</Warning>

***

### checkPushSubscription()

Check the push notification subscription status for the current user.

**Signature:** `tappd.checkPushSubscription(): Promise<{ subscribed: boolean; status: string; deviceCount: number }>`

<ResponseField name="subscribed" type="boolean">
  Whether the user has an active push subscription.
</ResponseField>

<ResponseField name="status" type="string">
  One of `'subscribed'`, `'opted_in'`, or `'unsubscribed'`.
</ResponseField>

<ResponseField name="deviceCount" type="number">
  Number of registered devices for this user.
</ResponseField>

```javascript theme={null}
const status = await tappd.checkPushSubscription();
console.log('Subscribed:', status.subscribed);
console.log('Status:', status.status);
console.log('Device Count:', status.deviceCount);
```

<Warning>
  Throws an error if the user is not identified.
</Warning>

***

### trackPushOpen(remoteMessage)

Track a push notification tap event. Call this inside your notification-opened handler to record that the user opened the app from a push notification.

**Signature:** `tappd.trackPushOpen(remoteMessage: object): Promise<void>`

```javascript theme={null}
messaging().onNotificationOpenedApp(async (remoteMessage) => {
  await tappd.trackPushOpen(remoteMessage);
});

// Also handle cold-start opens (app launched from a notification)
const initialMessage = await messaging().getInitialNotification();
if (initialMessage) {
  await tappd.trackPushOpen(initialMessage);
}
```

***

## Utility Methods

### getSessionId()

Return the current session ID.

**Signature:** `tappd.getSessionId(): string | null`

```javascript theme={null}
const sessionId = tappd.getSessionId();
console.log('Session ID:', sessionId);
// e.g., sess_abc123def456
```

***

### getAnonymousId()

Return the anonymous ID assigned before the user is identified. This value persists across app launches (via AsyncStorage) until the user is identified.

**Signature:** `tappd.getAnonymousId(): string`

```javascript theme={null}
const anonymousId = tappd.getAnonymousId();
console.log('Anonymous ID:', anonymousId);
// e.g., anon_xyz789ghi012
```

***

### reset()

Clear all user data and start a new anonymous session. Call this on user logout so subsequent events are not attributed to the previous user.

**Signature:** `tappd.reset(): void`

```javascript theme={null}
function handleLogout() {
  tappd.reset();
  // User is now anonymous again
}
```

***

### cleanup()

Flush final events, remove listeners, and cleanly shut down the SDK. Call this when your app moves to the background or closes.

**Signature:** `tappd.cleanup(): void`

```javascript theme={null}
import { AppState } from 'react-native';

AppState.addEventListener('change', (nextAppState) => {
  if (nextAppState === 'background') {
    tappd.cleanup();
  }
});
```

***

## In-App Message Methods

### setMessageRenderCallback(callback)

Register the function the SDK calls when it needs to render a message. This step is required in React Native — without it, messages are fetched but never displayed.

**Signature:** `tappd.setMessageRenderCallback(callback: (message: InAppMessage) => void): void`

```javascript theme={null}
import { MessageRenderer } from '@tappd/mobile-sdk/src/renderers/MessageRenderer';

const [currentMessage, setCurrentMessage] = useState(null);

tappd.setMessageRenderCallback((message) => {
  setCurrentMessage(message);
});

// In your JSX:
{currentMessage && (
  <MessageRenderer
    message={currentMessage}
    onDismiss={(id) => {
      tappd.dismissMessage(id);
      setCurrentMessage(null);
    }}
  />
)}
```

***

### getInAppMessages()

Fetch all pending in-app messages for the current user.

**Signature:** `tappd.getInAppMessages(): Promise<InAppMessage[]>`

```javascript theme={null}
const messages = await tappd.getInAppMessages();
messages.forEach((message) => {
  console.log(`Type: ${message.config.messageType}`);
  console.log(`Status: ${message.status}`);
});
```

***

### displayInAppMessage(message)

Display a specific message by passing the full `InAppMessage` object. This triggers the render callback you registered with `setMessageRenderCallback()`.

**Signature:** `tappd.displayInAppMessage(message: InAppMessage): Promise<void>`

```javascript theme={null}
const messages = await tappd.getInAppMessages();
if (messages.length > 0) {
  await tappd.displayInAppMessage(messages[0]);
}
```

***

### displayPendingMessages()

Fetch and display all messages that are ready to be shown in one call.

**Signature:** `tappd.displayPendingMessages(): Promise<void>`

```javascript theme={null}
await tappd.displayPendingMessages();
```

***

### dismissMessage(messageId)

Dismiss a message and automatically track the dismissal event.

**Signature:** `tappd.dismissMessage(messageId: string): Promise<void>`

```javascript theme={null}
await tappd.dismissMessage('message_id_123');
```

***

### trackMessageEvent(messageId, eventType, metadata?)

Track a custom interaction with an in-app message — for example, a button click inside the message.

**Signature:** `tappd.trackMessageEvent(messageId: string, eventType: string, metadata?: object): Promise<void>`

```javascript theme={null}
await tappd.trackMessageEvent('message_id_123', 'clicked', {
  buttonText: 'Get Started',
  buttonLink: '/signup'
});
```

***

## Banner Methods

### getBanners(selector, options?)

Fetch banners for a given placement selector.

**Signature:** `tappd.getBanners(selector: string, options?: { forceRefresh?: boolean }): Promise<Banner[]>`

```javascript theme={null}
const banners = await tappd.getBanners('home-hero', { forceRefresh: false });
```

***

### banners.display(bannerId, variantId, selector)

Display a banner variant in a named placement. Returns a `displayId` you use for subsequent click and dismiss calls.

**Signature:** `tappd.banners.display(bannerId: string, variantId: string, selector: string): Promise<string>`

```javascript theme={null}
const displayId = await tappd.banners.display('banner_abc', 'variant_1', 'home-hero');
```

***

### banners.click(bannerId, displayId)

Track a banner click event.

**Signature:** `tappd.banners.click(bannerId: string, displayId: string): Promise<void>`

```javascript theme={null}
await tappd.banners.click('banner_abc', displayId);
```

***

### banners.dismiss(bannerId, displayId)

Track a banner dismissal event.

**Signature:** `tappd.banners.dismiss(bannerId: string, displayId: string): Promise<void>`

```javascript theme={null}
await tappd.banners.dismiss('banner_abc', displayId);
```

***

## TypeScript Types

Use these interfaces to keep your integration fully type-safe.

<Accordion title="TappdConfig">
  ```typescript theme={null}
  interface TappdConfig {
    appId: string;                      // Required
    apiUrl?: string;                    // Optional — defaults to Tappd's hosted endpoint
    autoTrack?: boolean;                // default: true
    sessionTimeout?: number;            // default: 30 (minutes)
    enableAutoScreenTracking?: boolean; // default: true
    debug?: boolean;                    // default: false
    enableInAppMessages?: boolean;      // default: true
    autoDisplayMessages?: boolean;      // default: true
    messagePollingInterval?: number;    // default: 30 (seconds)
  }
  ```
</Accordion>

<Accordion title="CustomerAttributes">
  ```typescript theme={null}
  interface CustomerAttributes {
    external_id?: string;
    email?: string;
    phone?: string;
    alias?: {
      type: string;
      value: string;
    };
    name?: string;
    attributes?: {
      [key: string]: any;
    };
    [key: string]: any; // Additional flat custom attributes
  }
  ```

  At least one identifier (`external_id`, `email`, `phone`, or `alias`) is required. Store custom attributes either in the `attributes` object or as top-level properties.
</Accordion>

<Accordion title="EventProperties">
  ```typescript theme={null}
  interface EventProperties {
    userId?: string;
    user_id?: string;
    external_id?: string;
    externalId?: string;
    email?: string;
    phone?: string;
    alias?: {
      type: string;
      value: string;
    };
    sessionId?: string;
    [key: string]: any;
  }
  ```
</Accordion>

<Accordion title="InAppMessage">
  ```typescript theme={null}
  interface InAppMessage {
    id: string;
    status: string;
    config: {
      messageType: string;
      [key: string]: any;
    };
    [key: string]: any;
  }
  ```
</Accordion>

<Accordion title="Banner">
  ```typescript theme={null}
  interface Banner {
    id: string;
    variants: Array<{
      id: string;
      [key: string]: any;
    }>;
    [key: string]: any;
  }
  ```
</Accordion>

***

## Error Handling

All async SDK methods return promises and can throw. Wrap every call in a `try-catch` block to handle failures gracefully without crashing your app.

```javascript theme={null}
try {
  await tappd.identify({
    external_id: 'user_123',
    email: 'john@example.com'
  });
} catch (error) {
  console.error('Identification failed:', error);
}

try {
  await tappd.track('purchase', { amount: 99.99 });
} catch (error) {
  console.error('Tracking failed:', error);
}
```

**Common errors:**

| Error message                     | Cause                                                            | Fix                                          |
| --------------------------------- | ---------------------------------------------------------------- | -------------------------------------------- |
| `"App ID is required"`            | `appId` is missing or empty                                      | Copy your App ID from the Tappd dashboard    |
| `"User must be identified first"` | Called a method that requires identification before `identify()` | Call `identify()` first                      |
| `"Request failed: ..."`           | Network or API error                                             | Check your `apiUrl` and network connectivity |
| `"AsyncStorage error"`            | `@react-native-async-storage/async-storage` not installed        | Install the package and run `pod install`    |
