npm package: @flowly/sdk
Description: Flowly Attribution SDK — A lightweight, privacy-first solution for tracking marketing attribution, user events, and revenue across web applications. Built with TypeScript support and GDPR compliance in mind.
Author: flowly
Latest version: 0.0.20

# Flowly Attribution SDK

[![npm version](https://img.shields.io/npm/v/@flowly/sdk.svg)](https://www.npmjs.com/package/@flowly/sdk)
[![license](https://img.shields.io/npm/l/@flowly/sdk.svg)](https://www.npmjs.com/package/@flowly/sdk)

**Flowly Attribution SDK** helps you measure user acquisition, conversion events, and revenue with a privacy-first, lightweight approach. Ideal for modern web applications looking for accurate marketing attribution without sacrificing speed or user experience.

🌐 [Website](https://attribution.theflowly.com/) | 📚 [Documentation](https://attribution.theflowly.com/docs)

---

## Features

- 📈 **Accurate Attribution** — Track page visits, events, conversions, and revenue.
- 🔒 **Privacy-First** — GDPR-compliant by design.
- ⚡ **Lightweight & Fast** — Minimal performance impact.
- 🔧 **Highly Configurable** — Customize logging, session management, and more.
- 🚀 **Built for Developers** — TypeScript support out of the box.

---

## Installation

Install via npm or yarn:

```bash
npm install @flowly/sdk
# or
yarn add @flowly/sdk
```

Or use directly in the browser via CDN:

```html
<script src="https://cdn.jsdelivr.net/npm/@flowly/sdk@latest/dist/flowly-sdk.js"></script>
```

> Replace `latest` with the specific version number if needed.

When included this way, the SDK is available globally via the `Flowly` object.

Example:

```html
<script src="https://cdn.jsdelivr.net/npm/@flowly/sdk@0.0.9/dist/flowly-sdk.js"></script>
<script>
  const builder = Flowly.builder();
  const flowly = builder
    .apiKey('your-api-key')
    .secret('your-secret-key')
    .appId('your-product-id')
    .productName('your-product-name')
    .build();

  flowly.pageVisit('HomePage');
</script>
```

---

## Quick Start

```typescript
import { builder, LogLevel } from '@flowly/sdk';

const flowly = builder()
  .apiKey('your-api-key')
  .secret('your-secret-key')
  .appId('your-product-id')
  .productName('your-product-name')
  .logLevel(LogLevel.Info)
  .sessionTimeout(1800) // Optional: Session timeout in seconds
  .consentSettings({ // Optional: Configure user consent
    isConsentGiven: true,
    analyticsEnabled: true,
    adsPersonalizationEnabled: true
  })
  .build();

// Track page visit
flowly.pageVisit('HomePage');

// Track a custom event using EventBuilder
flowly.newEvent('Signup')
  .withParameter('plan', 'Pro')
  .track();

// Track a conversion event
flowly.conversionEvent('PurchaseCompleted', { orderId: '12345' });

// Track revenue
flowly.revenue('Purchase', 'USD', 99.99, { productId: 'sku_123' });

// Identify and login a user
flowly.login('custom-user-id');

// Logout the current user
flowly.logout();

// Flush pending events
flowly.flush();
```

---

## API Reference

### Builder Methods

| Method | Description |
| :--- | :--- |
| `apiKey(apiKey: string)` | Set the API key for authentication. |
| `secret(secret: string)` | Set the API secret for secure authentication. (Note: This is a JS-specific method for the builder. The common Kotlin FlowlyBuilder interface does not define `secret`.) |
| `appId(appId: string)` | Set your product ID. |
| `productName(productName: string)` | Set your product name. |
| `customUserId(customUserId: string)` | Set a custom user identifier. |
| `logLevel(logLevel: LogLevel)` | Define the logging level (Verbose, Debug, Info, etc.). |
| `sessionTimeout(sessionTimeout: number)` | Set session timeout (seconds). |
| `flowlyDeviceId(deviceId: string)` | Optionally set a custom device ID. |
| `autoPersistDomain(autoPersistDomain: string)` | Auto-persist across subdomains. |
| `baseUrl(baseUrl: string)` | Override the default API base URL. |
| `consentSettings(settings: ConsentSettings): FlowlyBuilder` | Configure initial user consent settings. |
| `attributionWindowConfig(config: AttributionWindowConfig): FlowlyBuilder` | Configure attribution window settings (e.g., click-through and view-through windows). |
| `build()` | Initialize and build the FlowlyController instance. |

### FlowlyController Methods

| Method | Description |
| :--- | :--- |
| `pageVisit(pageName: string)` | Track a page visit. |
| `event(eventName: string, args: any)` | Track a generic custom event. |
| `conversionEvent(eventName: string, args: any)` | Track a conversion-specific event. |
| `revenue(eventName: string, currency: string, amount: number, args: any)` | Track revenue-related events. |
| `login(customUserId: string)` | Identify and login a user. |
| `logout()` | Logout the current user. |
| `flush()` | Force send all pending events immediately. |
| `getDeviceId()` | Retrieve the device identifier used by the SDK. |
| `newEvent(eventName: string): EventBuilder` | Start building a custom event. Returns an EventBuilder. |
| `newConversionEvent(eventName: string): EventBuilder` | Start building a conversion event. Returns an EventBuilder. |
| `newRevenueEvent(eventName: string, currency: string, amount: number): EventBuilder` | Start building a revenue event. Returns an EventBuilder. |
| `getState(): ControllerState` | Get the current state of the SDK controller (e.g., STARTED, PAUSED). |
| `pause(): void` | Pause event tracking. Events will be queued or discarded based on SDK policy when paused. |
| `resume(): void` | Resume event tracking if previously paused. |
| `getConsentSettings(): ConsentSettings` | Get the current consent settings applied to the SDK. |
| `updateConsentSettings(settings: ConsentSettings): void` | Update user consent settings at runtime. Changes take effect immediately. |
| `getSessionId(): string` | Get the identifier for the current user session. |
| `getGlobalProperties(): any` | Get the currently set global properties that are automatically included with events. |
| `setGlobalProperties(properties: any): void` | Set global properties to be included with all subsequent events. |
| `handleDeepLink(url: string, callback?: (deepLinkData: DeepLinkData | null) => void): void` | Process a deep link URL. The optional callback receives the processed `DeepLinkData` or null. |
| `adRevenue(adPlatform: string, adSource: string, adUnitId: string, adFormat: string, value: number, currency: string, args?: object): void` | Track revenue generated from advertisements. |
| `getAttributionInfo(callback: (attributionData: AttributionData | null) => void): void` | Retrieves attribution information. The callback receives an AttributionData object or null. |
| `getAttributionData(): AttributionData | null` | Synchronously retrieve the resolved attribution information. May return null if data is not yet available. |
| `updatePostbackConversionValue(conversionValue: number, coarseValue?: string, lockWindow?: boolean): void` | (iOS SKAdNetwork) Update the conversion value. `coarseValue` and `lockWindow` are for SKAN 4.0. |
| `setPushToken(token: string, type: string): void` | Set the push notification token. `type` can be 'fcm', 'apns', 'hms', etc. (Common string values for `type` include "fcm" for Firebase Cloud Messaging, "apns" for Apple Push Notification service). |
| `enableUninstallTracking(enabled: boolean): void` | Enable or disable uninstall tracking (if supported by the platform and configuration). |
| `setInstallReferrer(referrer: string): void` | Set the install referrer string (e.g., from Play Store on Android or other sources). Should be called early. |
| `getLatestDeepLinkData(): DeepLinkData | null` | Synchronously retrieve the most recently processed deep link data. |
| `setEventSentCallback(callback: ((eventName: string, eventType: string, eventArgs: object | null) => void) | null): void` | Set a callback to be invoked after an event is processed by the SDK. |
| `setSessionStartCallback(callback: ((sessionId: string) => void) | null): void` | Set a callback to be invoked when a new session starts. |
| `verifyPurchase(purchaseInfo: PurchaseData, callback: (result: VerificationResult) => void): void` | Send purchase information for verification or tracking. |
| `trackSubscriptionStart(productId: string, details?: object): void` | Track the start of a subscription. |
| `trackSubscriptionRenewal(productId: string, details?: object): void` | Track a subscription renewal. |
| `trackSubscriptionCancel(productId: string, details?: object): void` | Track a subscription cancellation. |
| `trackTrialStart(productId: string, details?: object): void` | Track the start of a trial period. |

---
### E-commerce Events

Track specific e-commerce actions. These methods typically accept structured parameter objects.

| Method                                            | Description                                            |
| :------------------------------------------------ | :----------------------------------------------------- |
| `trackPurchase(params: ECommercePurchaseParams)`  | Track a completed purchase with detailed parameters.   |
| `trackViewItem(params: ECommerceViewItemParams)`  | Track a view of a specific item or product.          |
| `trackAddToCart(params: ECommerceAddToCartParams)`| Track an item being added to the cart.                 |

*(Note: `ECommercePurchaseParams`, `ECommerceViewItemParams`, and `ECommerceAddToCartParams` are typically plain JavaScript objects corresponding to complex structures defined in the core Kotlin SDK. Refer to Kotlin documentation for detailed structure if needed.)*

**Example for E-commerce:**
```javascript
// Assuming ECommercePurchaseParams is an object like:
// { transactionId: 'txn123', currency: 'USD', value: 59.99, items: [{itemId: 'p1', quantity: 1, price: 59.99}], ... }
flowlyInstance.trackPurchase({
  transactionId: 'T12345',
  value: 75.50,
  currency: 'USD',
  items: [
    { itemId: 'SKU001', itemName: 'Cool T-Shirt', quantity: 1, price: 25.00, category: 'Apparel' },
    { itemId: 'SKU002', itemName: 'Awesome Mug', quantity: 2, price: 12.75, category: 'Accessories' }
  ],
  shipping: 5.00,
  tax: 5.50
});

flowlyInstance.trackViewItem({
  items: [{ itemId: 'SKU003', itemName: 'Another Product', category: 'Gadgets', price: 100.00 }],
  currency: 'USD', // Optional if item prices include currency or a global one is assumed
  // other relevant parameters like item_list_name
});

flowlyInstance.trackAddToCart({
  items: [{ itemId: 'SKU004', itemName: 'Fancy Hat', quantity: 1, price: 30.00, category: 'Apparel' }],
  value: 30.00, // Total value of items added
  currency: 'USD'
});
```

### Utilities

| Function | Description |
| :--- | :--- |
| `isEnabled(): boolean` | Returns `true` if tracking is currently enabled. |
| `setEnabled(enabled: boolean)` | Enable or disable event tracking at runtime. |
| `isInitialized(): boolean` | Check if the SDK has been initialized. |
| `getInstance(): FlowlyController \| null` | Get the existing FlowlyController instance. |

### LogLevel Enum

Control the verbosity of SDK logs:

```typescript
LogLevel.Verbose  // Detailed logs
LogLevel.Debug    // Debugging information
LogLevel.Info     // General information
LogLevel.Warn     // Warnings
LogLevel.Error    // Errors
LogLevel.Assert   // Assertions
```

### EventBuilder Interface

The `EventBuilder` provides a fluent interface to construct and send events with multiple custom parameters. You obtain an `EventBuilder` instance by calling `newEvent()`, `newConversionEvent()`, or `newRevenueEvent()` on the `FlowlyController`.

| Method                               | Description                                       |
| :----------------------------------- | :------------------------------------------------ |
| `withParameter(key: string, value: any): EventBuilder` | Add a custom key-value parameter to the event.    |
| `track(): void`                      | Finalize and send the event.                      |

**Example:**

```typescript
flowly.newEvent('PlayerUpdate')
  .withParameter('level', 5)
  .withParameter('score', 1500)
  .track();
```

### ControllerState Enum

Represents the operational state of the Flowly SDK. Accessed via `flowly.getState()`.

| State         | Description                                                 |
| :------------ | :---------------------------------------------------------- |
| `NOT_STARTED` | The SDK has not been initialized or has been shut down.     |
| `STARTED`     | The SDK is initialized and actively tracking events.        |
| `PAUSED`      | The SDK is initialized but event tracking is temporarily paused. |

### ConsentSettings Object

The `ConsentSettings` object allows you to specify user consent preferences. It's used with `builder.consentSettings()` during initialization and `flowly.updateConsentSettings()` at runtime.

**Properties:**

| Property                  | Type    | Description                                                                 |
| :------------------------ | :------ | :-------------------------------------------------------------------------- |
| `isConsentGiven`          | boolean | General consent for tracking. If false, other flags are typically ignored.  |
| `analyticsEnabled`        | boolean | Consent for tracking general analytics and usage patterns.                  |
| `adsPersonalizationEnabled` | boolean | Consent for collecting data for personalized advertising.                 |
| `dataSharingEnabled`      | boolean | Consent for sharing data with third-party partners.                         |

**Example:**

```typescript
// To be used with builder.consentSettings() or flowly.updateConsentSettings()
const userConsent = {
  isConsentGiven: true,
  analyticsEnabled: true,
  adsPersonalizationEnabled: false // User opted out of ad personalization
};

// Example: Set initial consent during SDK build
// const flowly = builder()
//   .apiKey('your-api-key')
//   // ... other builder methods
//   .consentSettings(userConsent)
//   .build();

// Example: Update consent at runtime
// flowly.updateConsentSettings(userConsent);
```
*(Note: The `ConsentSettings` object in Kotlin has static helper methods `noConsent()` and `fullConsent()`. Their availability in JS depends on JsExport of companion objects. If not directly available, create the object manually as shown above.)*

### AttributionData Object

Describes the resolved attribution information for the user. Accessed via `flowly.getAttributionInfo()`.

| Property       | Type          | Description                                                                 |
| :------------- | :------------ | :-------------------------------------------------------------------------- |
| `network?`     | string        | The ad network or source that the attribution is credited to.               |
| `campaign?`    | string        | The specific campaign name.                                                 |
| `adgroup?`     | string        | The ad group name.                                                          |
| `creative?`    | string        | The ad creative name or identifier.                                         |
| `clickLabel?`  | string        | Unique identifier for the touchpoint (e.g., click ID).                      |
| `attributedAt?`| number        | Timestamp (milliseconds since epoch) when the attribution was resolved.     |
| `rawData?`     | object        | A map containing any other raw parameters associated with the attribution.  |

**Example:**
```typescript
// Structure of AttributionData object received in getAttributionInfo callback
// const attribution = {
//   network: "some_network",
//   campaign: "campaign_name",
//   attributedAt: 1678886400000, // Example timestamp
//   rawData: { custom_param: "value" }
// };
```

### PurchaseData Object

Used with `flowly.verifyPurchase()` to send purchase details.

| Property        | Type        | Description                                                                |
| :-------------- | :---------- | :------------------------------------------------------------------------- |
| `productId`     | string      | Identifier for the product purchased.                                      |
| `transactionId` | string      | Unique identifier for the transaction.                                     |
| `receiptData`   | string      | Receipt data (e.g., base64 encoded string, or JSON string from store).   |
| `currency`      | string      | ISO 4217 currency code for the transaction amount (e.g., "USD").           |
| `amount`        | number      | The monetary value of the transaction.                                     |
| `extras?`       | object      | Optional map for any additional custom key-value data related to the purchase. |

### VerificationResult Object

Received in the callback from `flowly.verifyPurchase()`.

| Property        | Type    | Description                                                                 |
| :-------------- | :------ | :-------------------------------------------------------------------------- |
| `success`       | boolean | Indicates if the SDK successfully processed/sent the verification request.  |
| `message?`      | string  | Optional message providing more details about the outcome.                  |
| `transactionId?`| string  | Echoes back the transaction ID from the `PurchaseData`.                     |


---

## Example

```typescript
import { builder, LogLevel, isInitialized, getInstance } from '@flowly/sdk';

if (!isInitialized()) {
  builder()
    .apiKey('your-api-key')
    .secret('your-secret-key')
    .appId('your-product-id')
    .productName('your-product-name')
    .logLevel(LogLevel.Warn)
    .sessionTimeout(1800)
    .consentSettings({ // Example: Configure user consent
      isConsentGiven: true,
      analyticsEnabled: true,
      adsPersonalizationEnabled: false // User opts out of ad personalization
    })
    .build();
}

const flowlyInstance = getInstance();
flowlyInstance?.pageVisit('LandingPage');

// Example of using EventBuilder
flowlyInstance?.newEvent('NewsletterSignup')
  .withParameter('newsletter', 'TechWeekly')
  .track();

// Example of setting an event callback
flowlyInstance?.setEventSentCallback((eventName, eventType, eventArgs) => {
  console.log(`Event Sent: ${eventType} - ${eventName}`, eventArgs || {});
});

// Example of getting attribution info (asynchronous)
flowlyInstance?.getAttributionInfo(function(attributionData) {
  if (attributionData) {
    console.log('Async Attribution - Network:', attributionData.network, 'Campaign:', attributionData.campaign);
  } else {
    console.log('Async Attribution: No data yet.');
  }
});

// Example of getting attribution data (synchronous)
const currentAttribution = flowlyInstance?.getAttributionData();
if (currentAttribution) {
  console.log('Sync Attribution - Network:', currentAttribution.network, 'Campaign:', currentAttribution.campaign);
} else {
  console.log('Sync Attribution: No data available or SDK not initialized.');
}

// Example of setting a push token
flowlyInstance?.setPushToken('your-push-token-here', 'fcm');
console.log('Push token set.');

// Example of tracking an E-commerce purchase event
flowlyInstance?.trackPurchase({
  transactionId: 'TXN789123',
  value: 120.50,
  currency: 'EUR',
  items: [
    { itemId: 'PROD456', itemName: 'Advanced Widget', quantity: 1, price: 100.00, category: 'Electronics' },
    { itemId: 'SERV01', itemName: 'Support Plan', quantity: 1, price: 20.50, category: 'Services' }
  ],
  shipping: 10.00,
  tax: 10.50
});
console.log('E-commerce purchase tracked.');
```

---

## Best Practices

- 🚀 **Initialize Early** — Initialize Flowly SDK as soon as your app loads to capture all events.
- 📖 **Use Meaningful Event Names** — Helps with better analytics and attribution reports.
- 🔒 **Respect Privacy** — Never log sensitive personal information.
- 📦 **Flush Before Exit** — Flush events before page unload or user logout to ensure data is sent.

---

## License

This project is licensed under the MIT License.

---

## Support

If you encounter any issues or have questions, please reach out:

- 📬 [Contact Us](https://attribution.theflowly.com/contact)

[//]: # (- 🛠️ [Submit an Issue]&#40;https://github.com/flowly-tech/flowly-sdk/issues&#41;)