npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

relay-kinetic

v1.3.0

Published

Headless SDK for tracking organic video attribution. Ask users which TikTok creator/video sent them, then attribute app installs to specific videos.

Readme

Relay Attribution SDK

Headless SDK for tracking organic video attribution. Ask users which TikTok creator/video sent them, then attribute app installs to specific videos.

Features:

  • 📊 Complete Funnel Tracking - Track every step from data request → modal shown → video selected → submission
  • 🔗 Automatic Event Linking - Device ID links all events across user journey
  • 🎯 Dual Purpose - Direct attribution + training data for Spot Lift statistical model
  • 📈 Rich Analytics - Answer questions like "What % of users who see options actually submit?"

Installation

npm install relay-kinetic

Quick Start

import { RelayAttribution } from 'relay-kinetic';

// 1. Initialize with your API key
// API key format: relay_prod_{yourBrandId}_{randomSuffix}
RelayAttribution.init({ apiKey: 'relay_prod_md770wapgetepmr3xsb5hys5hx7skmxs_abc123' });

// 2. Get top accounts (ranked by views)
// IMPORTANT: Include deviceId for automatic event tracking
const { creators } = await RelayAttribution.getTopAccounts(4, {
  deviceId: 'device_abc',        // Automatically tracks data_sent event
  adjustDeviceId: '355d5e...'    // Optional: Links with Adjust attribution
});
// Show picker with top 4 accounts

// 3. User selects account → Get their top videos
const { videos } = await RelayAttribution.getVideos('verily.payme', 4);
// Show grid with 4 videos (thumbnails + titles)

// 4. Track when modal is shown to user
await RelayAttribution.submit({
  eventType: 'modal_shown',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  videosShown: videos.map(v => v.videoId),  // Track which videos were shown
});

// 5. User selects video → Track selection + submit attribution
await RelayAttribution.submit({
  eventType: 'video_selected',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  selectedVideoId: 'ks7a2jf...',
  positionSelected: 0,           // Which position in list (0-indexed)
  timeToDecision: 5200,          // How long user took to decide (ms)
});

// 6. Submit final attribution
await RelayAttribution.submit({
  eventType: 'install',
  creator: 'verily.payme',
  videoId: 'ks7a2jf...',
  userId: 'user_123',
  deviceId: 'device_abc',
  ipAddress: '192.168.1.1',
  platform: 'ios',
  timestamp: Date.now(),
  metadata: {
    appVersion: '1.2.3',
    referralCode: 'ABC123',
  }
});

Event Tracking

The SDK tracks a complete funnel from data request to final attribution. All events are linked via deviceId to enable powerful analytics.

Event Types

1. data_sent (Automatic)

  • When: Automatically tracked when you call GET endpoints with deviceId
  • Purpose: Track what data was sent to users (which creators/videos shown)
  • Required Fields: None - automatic when deviceId provided to GET methods

2. modal_shown (Manual)

  • When: Track when attribution modal is displayed to user
  • Purpose: Measure modal view rate, understand drop-off
  • Required Fields: videosShown (array of video IDs)

3. modal_dismissed (Manual)

  • When: Track when user closes modal without selection
  • Purpose: Understand abandonment rate
  • Required Fields: videosShown, optional timeOnModal

4. video_selected (Manual)

  • When: Track when user selects a specific video
  • Purpose: Understand selection patterns, position bias
  • Required Fields: selectedVideoId, positionSelected

5. other_selected (Manual)

  • When: Track when user selects "Other" / "None of these"
  • Purpose: Measure relevance of shown options
  • Required Fields: videosShown

6. install / purchase / signup (Manual)

  • When: Track attribution events (installs, conversions)
  • Purpose: Direct attribution to specific videos
  • Required Fields: creator, videoId, ipAddress, timestamp

7. submission_success / submission_failure (Automatic)

  • When: Automatically tracked when attribution submission completes
  • Purpose: Monitor API reliability, debug issues
  • Required Fields: None - automatic

Funnel Analytics Examples

Track complete user journey:

// Step 1: User requests data (automatic tracking)
const { creators } = await RelayAttribution.getTopAccounts(4, {
  deviceId: 'device_abc'  // → Logs data_sent event
});

// Step 2: Modal shown to user
await RelayAttribution.submit({
  eventType: 'modal_shown',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  videosShown: ['video1', 'video2', 'video3'],
});

// Step 3a: User selects video
await RelayAttribution.submit({
  eventType: 'video_selected',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  selectedVideoId: 'video1',
  positionSelected: 0,
  timeToDecision: 5200,
});

// Step 3b: OR user selects "Other"
await RelayAttribution.submit({
  eventType: 'other_selected',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  videosShown: ['video1', 'video2', 'video3'],
  timeToDecision: 3100,
});

// Step 4: Final attribution (automatic success/failure tracking)
await RelayAttribution.submit({
  eventType: 'install',
  creator: 'kmanbiv',
  videoId: 'video1',
  deviceId: 'device_abc',
  userId: 'user_123',
  ipAddress: '192.168.1.1',
  platform: 'ios',
  timestamp: Date.now(),
});

Questions you can answer with event data:

  • What % of users who receive data actually submit attribution?
  • What % of users who see modal make a selection?
  • Which position in list gets selected most often?
  • How long do users spend deciding?
  • Are the shown videos relevant to users?
  • What's the conversion rate from data sent → attribution?

Getting Your API Key

Contact Relay to get your API key. Format: relay_prod_{yourBrandId}_{suffix}

The brandId is extracted from your key to filter data for your brand.

API

init(config)

Initialize the SDK with your API key.

RelayAttribution.init({
  apiKey: 'relay_prod_yourBrandId_xyz',
  baseUrl: 'https://relay-api.railway.app' // Optional, defaults to production
});

getCreators()

Get list of all creators for your brand.

const { creators } = await RelayAttribution.getCreators();
// Returns all creators (unranked)

getTopAccounts(limit?, options)

Get top N accounts (creators) ranked by total views.

const { creators } = await RelayAttribution.getTopAccounts(4, {
  deviceId: 'device_abc',        // Required for event tracking
  adjustDeviceId: '355d5e...'    // Optional
});

// Returns:
// {
//   creators: [
//     {
//       username: "kmanbiv",
//       displayName: "Kevin",
//       avatar: "https://...",
//       videoCount: 45,
//       totalViews: 1200000
//     }
//   ]
// }

Parameters:

  • limit (optional): Number of accounts to return (default: 4, max: 20)
  • options (required): Event tracking parameters
    • deviceId (required): Device identifier for automatic data_sent event tracking
    • adjustDeviceId (optional): Adjust device ID for attribution linking

getTrendingVideos(limit?, options)

Get trending videos across all creators. Videos can be filtered by time window (e.g., last 24 hours) or show all-time trending.

// Get trending videos from last 24 hours
const { videos, hours } = await RelayAttribution.getTrendingVideos(6, {
  deviceId: 'device_abc',        // Required for event tracking
  adjustDeviceId: '355d5e...',   // Optional
  hours: 24                      // Optional: time window
});

// Get all-time trending videos
const { videos } = await RelayAttribution.getTrendingVideos(6, {
  deviceId: 'device_abc'
});

// Returns:
// {
//   hours: 24,  // null for all-time
//   videos: [
//     {
//       videoId: "ns72c1x...",
//       platformVideoId: "7123...",
//       url: "https://www.tiktok.com/@kmanbiv/video/123",
//       thumbnail: "https://pub-...r2.dev/thumbnails/video_123.jpg",
//       title: "#cabinfever #covid19",
//       username: "kmanbiv",
//       recentViews: 15000,     // Views in time window (or totalViews for all-time)
//       totalViews: 299800,
//       publishedAt: "2020-03-30T22:58:05.000Z"
//     }
//   ]
// }

Parameters:

  • limit (optional): Number of videos to return (default: 6, max: 20)
  • options (required): Event tracking parameters
    • deviceId (required): Device identifier for automatic data_sent event tracking
    • adjustDeviceId (optional): Adjust device ID for attribution linking
    • hours (optional): Time window for trending calculation (e.g., 24 for last 24 hours). Omit for all-time trending.

getVideos(creator, limit?)

Get top N performing videos for a specific creator.

const { videos } = await RelayAttribution.getVideos('kmanbiv', 6);
// Returns:
// {
//   creator: "kmanbiv",
//   videos: [
//     {
//       videoId: "ns72c1x...",
//       url: "https://www.tiktok.com/@kmanbiv/video/123",
//       thumbnail: "https://pub-...r2.dev/thumbnails/video_123.jpg",
//       hook: "Make $200 from home",
//       title: "#cabinfever #covid19",
//       viewCount: 299800,
//       publishedAt: "2020-03-30T22:58:05.000Z"
//     }
//   ]
// }

Parameters:

  • creator (required): Creator username
  • limit (optional): Number of videos to return (default: 4, max: 20)

submit(data)

Submit attribution events or track user interactions. Supports 7 event types.

Attribution Events (install, purchase, signup)

For initial install events, include creator and videoId:

// Initial install event - include creator + videoId
await RelayAttribution.submit({
  eventType: 'install',         // 'install' | 'purchase' | 'signup'
  creator: 'kmanbiv',           // Required for install: Selected creator username
  videoId: 'ns72c1x...',        // Required for install: Selected video ID
  userId: 'user_123',           // Required: Your app's user ID
  deviceId: 'device_abc',       // Required: Device identifier (links events together)
  ipAddress: '192.168.1.1',     // Required: User's IP address
  platform: 'ios',              // Required: 'ios' | 'android'
  timestamp: Date.now(),        // Required: Event timestamp
  adjustDeviceId: '355d5e...',  // Optional: Adjust device ID (if available)
  appVersion: '1.2.3',          // Optional: App version
  metadata: {                   // Optional: Custom app data (any JSON object)
    referralCode: 'ABC123',
    userTier: 'premium',
  }
});

For follow-up events (purchase, signup), you can omit creator and videoId - they'll be auto-linked:

// Follow-up purchase event - creator/videoId auto-linked via deviceId
await RelayAttribution.submit({
  eventType: 'purchase',
  userId: 'user_123',
  deviceId: 'device_abc',       // Must match original install deviceId
  ipAddress: '192.168.1.1',
  platform: 'ios',
  timestamp: Date.now(),
  revenue: 9.99,                // Include revenue for purchase events
  currency: 'USD',
});

Interaction Events

Track user interactions with attribution modal:

// 1. Modal shown to user
await RelayAttribution.submit({
  eventType: 'modal_shown',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  videosShown: ['video1', 'video2', 'video3', 'video4'],
});

// 2. User dismissed modal without selecting
await RelayAttribution.submit({
  eventType: 'modal_dismissed',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  videosShown: ['video1', 'video2', 'video3', 'video4'],
  timeOnModal: 3500,  // Optional: Time user spent viewing modal (ms)
});

// 3. User selected a video
await RelayAttribution.submit({
  eventType: 'video_selected',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  selectedVideoId: 'video1',
  positionSelected: 0,         // Which position in list (0-indexed)
  timeToDecision: 5200,        // Optional: Time from modal open to selection (ms)
  videosShown: ['video1', 'video2', 'video3', 'video4'],  // Optional
});

// 4. User selected "Other" / "None of these"
await RelayAttribution.submit({
  eventType: 'other_selected',
  deviceId: 'device_abc',
  userId: 'user_123',
  platform: 'ios',
  videosShown: ['video1', 'video2', 'video3', 'video4'],
  timeToDecision: 3100,        // Optional: Time from modal open to selection (ms)
});

TypeScript Support

Fully typed with TypeScript. Import types:

import type {
  Creator,
  Video,
  AttributionSubmission,
  AttributionEvent,
  ModalShownEvent,
  ModalDismissedEvent,
  VideoSelectedEvent,
  OtherSelectedEvent,
  AttributionResponse
} from 'relay-kinetic';

The AttributionSubmission type is a discriminated union of all event types, enabling full type safety based on eventType.

Error Handling

try {
  await RelayAttribution.submit(data);
} catch (error) {
  console.error('Attribution failed:', error.message);
}

React Native Example

import { useState, useEffect, useRef } from 'react';
import { View, FlatList, TouchableOpacity, Image, Text, Platform } from 'react-native';
import { RelayAttribution } from 'relay-kinetic';
import DeviceInfo from 'react-native-device-info';

function AttributionSurvey({ userId, onComplete }) {
  const [creators, setCreators] = useState([]);
  const [selectedCreator, setSelectedCreator] = useState(null);
  const [videos, setVideos] = useState([]);
  const modalOpenTime = useRef(null);

  const deviceId = DeviceInfo.getUniqueId();

  useEffect(() => {
    RelayAttribution.init({ apiKey: process.env.RELAY_API_KEY });
    loadCreators();
  }, []);

  const loadCreators = async () => {
    // Automatically tracks data_sent event
    const { creators } = await RelayAttribution.getTopAccounts(4, {
      deviceId,
      adjustDeviceId: await getAdjustDeviceId(), // Optional
    });
    setCreators(creators);
  };

  const selectCreator = async (username) => {
    setSelectedCreator(username);
    const { videos } = await RelayAttribution.getVideos(username, 6);
    setVideos(videos);

    // Track modal shown
    modalOpenTime.current = Date.now();
    await RelayAttribution.submit({
      eventType: 'modal_shown',
      deviceId,
      userId,
      platform: Platform.OS,
      videosShown: videos.map(v => v.videoId),
    });
  };

  const selectVideo = async (video, index) => {
    const timeToDecision = Date.now() - modalOpenTime.current;

    // Track video selection
    await RelayAttribution.submit({
      eventType: 'video_selected',
      deviceId,
      userId,
      platform: Platform.OS,
      selectedVideoId: video.videoId,
      positionSelected: index,
      timeToDecision,
      videosShown: videos.map(v => v.videoId),
    });

    // Submit final attribution
    await RelayAttribution.submit({
      eventType: 'install',
      creator: selectedCreator,
      videoId: video.videoId,
      userId,
      deviceId,
      ipAddress: await getIpAddress(),
      platform: Platform.OS,
      timestamp: Date.now(),
    });

    onComplete();
  };

  const selectOther = async () => {
    const timeToDecision = Date.now() - modalOpenTime.current;

    // Track "Other" selection
    await RelayAttribution.submit({
      eventType: 'other_selected',
      deviceId,
      userId,
      platform: Platform.OS,
      videosShown: videos.map(v => v.videoId),
      timeToDecision,
    });

    onComplete();
  };

  const dismissModal = async () => {
    const timeOnModal = Date.now() - modalOpenTime.current;

    // Track modal dismissal
    await RelayAttribution.submit({
      eventType: 'modal_dismissed',
      deviceId,
      userId,
      platform: Platform.OS,
      videosShown: videos.map(v => v.videoId),
      timeOnModal,
    });

    onComplete();
  };

  if (!selectedCreator) {
    return (
      <FlatList
        data={creators}
        renderItem={({ item }) => (
          <TouchableOpacity onPress={() => selectCreator(item.username)}>
            <Text>{item.displayName}</Text>
          </TouchableOpacity>
        )}
      />
    );
  }

  return (
    <View>
      <FlatList
        data={videos}
        numColumns={2}
        renderItem={({ item, index }) => (
          <TouchableOpacity onPress={() => selectVideo(item, index)}>
            <Image source={{ uri: item.thumbnail }} />
            <Text>{item.hook}</Text>
          </TouchableOpacity>
        )}
      />
      <TouchableOpacity onPress={selectOther}>
        <Text>Other / None of these</Text>
      </TouchableOpacity>
      <TouchableOpacity onPress={dismissModal}>
        <Text>Dismiss</Text>
      </TouchableOpacity>
    </View>
  );
}

Bundle Size

~2KB gzipped. Zero dependencies.

License

MIT