> ## 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 Web SDK API Reference: Methods and TypeScript Types

> Complete reference for every Tappd Web SDK method — identify, track, page views, session utilities, banners, in-app messages, and push notifications.

The Tappd Web SDK exposes a set of async methods that let you identify users, track events, manage sessions, render banners, display in-app messages, and handle push notifications. Every async method returns a `Promise`, so wrap calls in `try/catch` or chain `.catch()` to handle errors gracefully.

***

## Constructor

### new TappdSDK()

Create and initialize an SDK instance. Call this once — typically at the top level of your app — and reuse the returned instance everywhere.

```typescript theme={null}
new TappdSDK(config: TappdConfig)
```

<ParamField body="config" type="TappdConfig" required>
  SDK configuration object. `appId` is the only required field.
</ParamField>

**Returns:** `TappdSDK` instance.

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

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

***

## Core Methods

### identify()

Identify or update a customer using one or more identifiers. When you call `identify()`, the SDK automatically merges any anonymous events tracked in the current session with the resolved customer profile.

<ParamField body="attributes" type="CustomerAttributes" required>
  User attributes object. Must include at least one of `external_id`, `email`, `phone`, or `alias`.
</ParamField>

**Returns:** `Promise<void>`

**Lookup priority:** `external_id` → `email` → `phone` → `alias`

<Note>
  Use `external_id` whenever possible — it's the most stable and reliable identifier. Include multiple identifiers to improve match accuracy across devices and sessions.
</Note>

**CustomerAttributes interface:**

```typescript theme={null}
interface CustomerAttributes {
  external_id?: string;       // Your internal user ID (recommended)
  email?: string;             // User email — auto-normalized (lowercased, trimmed)
  phone?: string;             // User phone number
  alias?: {                   // Custom alias identifier
    type: string;             // e.g. 'session_id', 'legacy_user_id'
    value: string;
  };
  name?: string;
  attributes?: {              // Nested custom attributes object
    [key: string]: any;
  };
  [key: string]: any;         // Top-level custom attributes (flat)
}
```

**Identification examples:**

<CodeGroup>
  ```javascript External ID (Recommended) theme={null}
  // Use your database user ID as the primary identifier
  await tappd.identify({
    external_id: 'user_123',
    name: 'Jane Smith',
    email: 'jane@example.com',
    phone: '+1234567890',
    plan: 'premium',
    company: 'Acme Corp'
  });
  ```

  ```javascript Email Only theme={null}
  await tappd.identify({
    email: 'user@example.com',
    name: 'Jane Smith',
    plan: 'free'
  });
  ```

  ```javascript Phone Only theme={null}
  await tappd.identify({
    phone: '+1234567890',
    name: 'Jane Smith',
    plan: 'free'
  });
  ```

  ```javascript Alias theme={null}
  await tappd.identify({
    alias: {
      type: 'session_id',   // Custom types are stored as 'custom'
      value: '1234'
    },
    name: 'Jane Smith',
    plan: 'free'
  });
  ```

  ```javascript Multiple Identifiers (Recommended) theme={null}
  // Provide all available identifiers for the best match rate
  await tappd.identify({
    external_id: 'user_123',
    email: 'jane@example.com',
    phone: '+1234567890',
    name: 'Jane Smith',
    attributes: {
      plan: 'premium',
      company: 'Acme Corp'
    }
  });
  ```
</CodeGroup>

**How identification works:**

<Steps>
  <Step title="Find or create customer">
    The SDK searches for an existing customer using the lookup priority order. If no match is found, it creates a new profile.
  </Step>

  <Step title="Associate events">
    All future `track()` calls are automatically linked to this customer — no need to pass identifiers again.
  </Step>

  <Step title="Merge anonymous data">
    Any events tracked before `identify()` are automatically merged into the resolved customer profile.
  </Step>

  <Step title="Link sessions">
    The current session and all future sessions are attached to the identified user.
  </Step>
</Steps>

***

### track()

Track a custom event with an optional properties payload. If the user is already identified, the SDK automatically associates the event — you don't need to repeat identifiers in the properties.

```typescript theme={null}
track(eventName: string, properties?: EventProperties): Promise<void>
```

<ParamField body="eventName" type="string" required>
  The name of the event to track (e.g. `'purchase'`, `'button_clicked'`).
</ParamField>

<ParamField body="properties" type="EventProperties">
  Optional event properties. Can include identifiers to associate the event with a specific customer.
</ParamField>

**Returns:** `Promise<void>`

**Identifier lookup order in properties:** `userId`/`user_id` → `external_id`/`externalId` → `email` → `phone` → `alias`

<CodeGroup>
  ```javascript After identify() — no identifier needed theme={null}
  // Once identified, all events are linked automatically
  await tappd.identify({
    external_id: 'user_123',
    email: 'jane@example.com'
  });

  await tappd.track('purchase', {
    amount: 99.99,
    currency: 'USD',
    productId: 'prod_123',
    productName: 'Pro Plan'
  });
  ```

  ```javascript With inline identifier override theme={null}
  // Associate a one-off event with a specific user
  await tappd.track('checkout_started', {
    external_id: 'user_123',
    email: 'jane@example.com',
    cartValue: 149.99,
    items: 3
  });
  ```
</CodeGroup>

***

### trackPageView()

Manually track a page view. If you set `autoTrack: true` during initialization, the SDK handles this automatically on every navigation. Use this method when you need finer control — for example, in custom SPA routers.

```typescript theme={null}
trackPageView(url?: string, properties?: EventProperties): Promise<void>
```

<ParamField body="url" type="string">
  The URL to record. Defaults to `window.location.href` if omitted.
</ParamField>

<ParamField body="properties" type="EventProperties">
  Optional additional properties to attach to the page view event.
</ParamField>

**Returns:** `Promise<void>`

<CodeGroup>
  ```javascript Current page theme={null}
  // Records window.location.href automatically
  await tappd.trackPageView();
  ```

  ```javascript Specific URL with referrer theme={null}
  await tappd.trackPageView('https://example.com/pricing', {
    referrer: 'google.com',
    campaign: 'spring_sale'
  });
  ```
</CodeGroup>

***

### setExternalId()

Set or update the `external_id` for the currently identified user. The user must already be identified — call `identify()` first.

```typescript theme={null}
setExternalId(externalId: string): Promise<void>
```

<ParamField body="externalId" type="string" required>
  Your internal user identifier.
</ParamField>

**Returns:** `Promise<void>`

**Throws:** `Error` — `"User must be identified first"` if `identify()` has not been called.

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

***

### setUserAttributes()

Update attributes for the currently identified user. The user must already be identified.

```typescript theme={null}
setUserAttributes(attributes: CustomerAttributes): Promise<void>
```

<ParamField body="attributes" type="CustomerAttributes" required>
  Attributes to update on the customer profile.
</ParamField>

**Returns:** `Promise<void>`

**Throws:** `Error` — `"User must be identified first"`

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

***

### setCustomAttributes()

Set or overwrite custom attributes on the currently identified user's profile.

```typescript theme={null}
setCustomAttributes(attributes: Record<string, any>): Promise<void>
```

<ParamField body="attributes" type="Record<string, any>" required>
  Key-value map of custom attributes to apply.
</ParamField>

**Returns:** `Promise<void>`

**Throws:** `Error` — `"User must be identified first"`

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

***

## Utility Methods

### getSessionId()

Return the active session ID. Sessions are created automatically on initialization and resume when a hidden tab becomes visible again.

```typescript theme={null}
getSessionId(): string | null
```

**Returns:** `string | null` — the current session ID, or `null` if no session is active.

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

***

### getAnonymousId()

Return the anonymous ID for the current browser context. This ID persists in `localStorage` across page loads and sessions until `identify()` is called.

```typescript theme={null}
getAnonymousId(): string
```

**Returns:** `string` — the anonymous ID.

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

***

### reset()

Clear all user data and start a fresh session. Call this on logout to prevent the next user on the same device from inheriting the previous session's identity.

```typescript theme={null}
reset(): void
```

**Returns:** `void`

```javascript theme={null}
function handleLogout() {
  tappd.reset();
  // The SDK is now anonymous — ready for the next user
}
```

***

## Banners

### getBanners()

Fetch banners targeted to the current user for a given CSS selector. By default the SDK renders the returned banners automatically into the matched element.

```typescript theme={null}
getBanners(
  selector: string,
  options?: { autoRender?: boolean; forceRefresh?: boolean }
): Promise<Banner[]>
```

<ParamField body="selector" type="string" required>
  A CSS selector string that identifies the DOM element(s) where banners should be rendered (e.g. `'#hero-banner'`, `'.promo-slot'`).
</ParamField>

<ParamField body="options" type="object">
  Optional rendering options.

  * `autoRender` — automatically inject the banner HTML into the matched element. Defaults to `true`.
  * `forceRefresh` — bypass the local cache and fetch fresh banners from the API. Defaults to `false`.
</ParamField>

**Returns:** `Promise<Banner[]>`

```javascript theme={null}
// Fetch and auto-render banners into #hero-banner
const banners = await tappd.getBanners('#hero-banner');

// Fetch without auto-rendering so you can handle HTML yourself
const banners = await tappd.getBanners('.promo-slot', { autoRender: false });
banners.forEach(banner => {
  document.querySelector('.promo-slot').innerHTML = banner.html;
});

// Force a fresh fetch, ignoring any cached results
const banners = await tappd.getBanners('#sidebar-banner', { forceRefresh: true });
```

***

### banners.display()

Manually record a banner display impression and return a unique display ID. Use this when `autoRender` is `false` and you render the banner HTML yourself.

```typescript theme={null}
tappd.banners.display(
  bannerId: string,
  variantId: string,
  selector: string
): Promise<string>
```

<ParamField body="bannerId" type="string" required>
  The ID of the banner to record a display for.
</ParamField>

<ParamField body="variantId" type="string" required>
  The ID of the specific banner variant being shown.
</ParamField>

<ParamField body="selector" type="string" required>
  The CSS selector of the container element where the banner is displayed.
</ParamField>

**Returns:** `Promise<string>` — a unique display ID used for subsequent click and dismiss calls.

```javascript theme={null}
const displayId = await tappd.banners.display('banner_123', 'variant_abc', '#hero-banner');
console.log('Display recorded:', displayId);
```

***

### banners.click()

Track a click interaction on a displayed banner. Call this when the user clicks on the banner or a CTA within it.

```typescript theme={null}
tappd.banners.click(bannerId: string, displayId: string): Promise<void>
```

<ParamField body="bannerId" type="string" required>
  The ID of the banner that was clicked.
</ParamField>

<ParamField body="displayId" type="string" required>
  The display ID returned by `banners.display()`.
</ParamField>

**Returns:** `Promise<void>`

```javascript theme={null}
bannerElement.addEventListener('click', async () => {
  await tappd.banners.click('banner_123', displayId);
});
```

***

### banners.dismiss()

Record that the user dismissed a banner. Call this when the user closes or hides the banner without clicking through.

```typescript theme={null}
tappd.banners.dismiss(bannerId: string, displayId: string): Promise<void>
```

<ParamField body="bannerId" type="string" required>
  The ID of the banner being dismissed.
</ParamField>

<ParamField body="displayId" type="string" required>
  The display ID returned by `banners.display()`.
</ParamField>

**Returns:** `Promise<void>`

```javascript theme={null}
closeButton.addEventListener('click', async () => {
  await tappd.banners.dismiss('banner_123', displayId);
});
```

***

## In-App Messages

### getInAppMessages()

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

```typescript theme={null}
getInAppMessages(): Promise<InAppMessage[]>
```

**Returns:** `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()

Display a specific in-app message object.

```typescript theme={null}
displayInAppMessage(message: InAppMessage): Promise<void>
```

<ParamField body="message" type="InAppMessage" required>
  The message object returned by `getInAppMessages()`.
</ParamField>

**Returns:** `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 currently ready to show. Use this as a shortcut instead of calling `getInAppMessages()` and `displayInAppMessage()` separately.

```typescript theme={null}
displayPendingMessages(): Promise<void>
```

**Returns:** `Promise<void>`

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

***

### dismissMessage()

Dismiss a specific message and record the dismissal event in the Tappd dashboard.

```typescript theme={null}
dismissMessage(messageId: string): Promise<void>
```

<ParamField body="messageId" type="string" required>
  The ID of the message to dismiss.
</ParamField>

**Returns:** `Promise<void>`

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

***

### trackMessageEvent()

Track a custom interaction event for a specific in-app message — for example, a button click or a custom CTA action.

```typescript theme={null}
trackMessageEvent(
  messageId: string,
  eventType: string,
  metadata?: object
): Promise<void>
```

<ParamField body="messageId" type="string" required>
  The ID of the message.
</ParamField>

<ParamField body="eventType" type="string" required>
  Event type string — e.g. `'viewed'`, `'clicked'`, `'dismissed'`.
</ParamField>

<ParamField body="metadata" type="object">
  Optional metadata to attach to the event.
</ParamField>

**Returns:** `Promise<void>`

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

***

## Push Notifications

### getVapidPublicKey()

Retrieve the VAPID public key required for setting up web push subscriptions manually.

```typescript theme={null}
getVapidPublicKey(): Promise<string>
```

**Returns:** `Promise<string>` — the VAPID public key.

```javascript theme={null}
const publicKey = await tappd.getVapidPublicKey();
console.log('VAPID Public Key:', publicKey);
```

***

### isSubscribed()

Check whether the current user is already subscribed to push notifications.

```typescript theme={null}
isSubscribed(): Promise<boolean>
```

**Returns:** `Promise<boolean>` — `true` if subscribed, `false` otherwise.

```javascript theme={null}
const subscribed = await tappd.isSubscribed();
if (subscribed) {
  console.log('User is subscribed to push notifications.');
} else {
  console.log('User is not yet subscribed.');
}
```

***

### subscribeToPush()

Subscribe the currently identified user to push notifications. You must call `identify()` before subscribing.

```typescript theme={null}
subscribeToPush(options?: PushSubscriptionOptions): Promise<PushSubscription>
```

<ParamField body="options" type="PushSubscriptionOptions">
  Optional subscription configuration passed to the browser's Push API.
</ParamField>

**Returns:** `Promise<PushSubscription>`

**Throws:** `Error` if:

* Push notifications are not supported in the current browser
* The user has denied notification permission
* `identify()` has not been called

```javascript theme={null}
// Always identify the user before subscribing
await tappd.identify({
  external_id: 'user_123',
  email: 'jane@example.com'
});

try {
  const subscription = await tappd.subscribeToPush();
  console.log('Subscribed:', subscription.endpoint);
} catch (error) {
  console.error('Subscription failed:', error.message);
}
```

***

### unsubscribeFromPush()

Remove the current user's push notification subscription.

```typescript theme={null}
unsubscribeFromPush(): Promise<void>
```

**Returns:** `Promise<void>`

```javascript theme={null}
try {
  await tappd.unsubscribeFromPush();
  console.log('Successfully unsubscribed.');
} catch (error) {
  console.error('Unsubscribe failed:', error.message);
}
```

***

### showPermissionPrompt()

Display a push notification permission prompt. Choose the style that best fits your UI.

```typescript theme={null}
showPermissionPrompt(type: 'slidedown' | 'bell' | 'native'): Promise<void>
```

<ParamField body="type" type="'slidedown' | 'bell' | 'native'" required>
  The prompt style to display. Use `'native'` for the browser's built-in dialog, `'slidedown'` for a banner, or `'bell'` for an icon-triggered prompt.
</ParamField>

**Returns:** `Promise<void>`

<Tip>
  After showing the prompt, call `subscribeToPush()` when the user grants permission to complete the subscription flow.
</Tip>

```javascript theme={null}
// Show a slidedown banner
await tappd.showPermissionPrompt('slidedown');

// Or use the native browser dialog
await tappd.showPermissionPrompt('native');
```

***

### hidePermissionPrompt()

Programmatically hide any active permission prompt.

```typescript theme={null}
hidePermissionPrompt(): Promise<void>
```

**Returns:** `Promise<void>`

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

***

## TypeScript Types

Use these interfaces to get full type safety throughout your project.

### TappdConfig

```typescript theme={null}
interface TappdConfig {
  appId: string;                    // Required — your Tappd app identifier
  apiUrl?: string;                  // Optional — custom API endpoint
  autoTrack?: boolean;              // Optional — default: true
  sessionTimeout?: number;          // Optional — minutes, default: 30
  enableAutoCapture?: boolean;      // Optional — default: false
  debug?: boolean;                  // Optional — default: false
  enableInAppMessages?: boolean;    // Optional — default: true
  autoDisplayMessages?: boolean;    // Optional — default: true
  messagePollingInterval?: number;  // Optional — seconds, default: 30
  messageContainerId?: string;      // Optional — custom DOM container ID
}
```

### CustomerAttributes

```typescript theme={null}
interface CustomerAttributes {
  external_id?: string;   // Your internal user ID (recommended)
  email?: string;         // Auto-normalized (lowercased, trimmed)
  phone?: string;
  alias?: {
    type: string;         // e.g. 'session_id', 'legacy_user_id'
    value: string;
  };
  name?: string;
  attributes?: {          // Nested custom attributes
    [key: string]: any;
  };
  [key: string]: any;     // Top-level custom attributes (flat)
}
```

### EventProperties

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

***

## Error Handling

All async methods can throw. Always wrap calls in `try/catch` to handle errors without breaking your application.

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

try {
  await tappd.track('purchase', { amount: 99.99, currency: 'USD' });
} catch (error) {
  console.error('Event tracking failed:', error.message);
}
```

**Common error messages:**

| Error Message                     | Cause                                                            | Fix                                                             |
| --------------------------------- | ---------------------------------------------------------------- | --------------------------------------------------------------- |
| `"App ID is required"`            | `appId` is missing or empty in the config                        | Provide a valid `appId` from your Tappd dashboard               |
| `"User must be identified first"` | Called a method that requires identification before `identify()` | Call `identify()` with at least one valid identifier first      |
| `"Request failed: ..."`           | Network failure or API error                                     | Check your `apiUrl`, network connection, and CORS configuration |
