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

@avibra/pulse-web-sdk

v1.1.4

Published

Pulse — AI voice feedback SDK for third-party web applications

Readme

Pulse SDK — AI-Powered Voice Feedback

Embed AI-powered voice feedback conversations into any web application in minutes.

npm version gzip size TypeScript License: MIT


Table of Contents


Overview

Pulse is an AI-powered voice feedback platform. When a user performs a significant action — cancels a subscription, completes onboarding, or churns — Pulse surfaces a conversational voice prompt so they can tell you why in their own words.

Two integration models:

| Model | How it works | |---|---| | SDK Tracking | Call Pulse.trackEvent() — SDK sends event to Pulse backend which evaluates triggers | | Analytics Webhook | Mixpanel / Segment / Braze forwards events to Pulse backend; SDK polls for pending triggers |


Quick Start

CDN (Recommended for most partners)

<!-- Add before </body> -->
<script src="https://cdn.avibra.com/sdk/v1/pulse.min.js"></script>
<script>
  Pulse.init({
    partnerId: 'YOUR_PARTNER_ID',
    userId: currentUser.id,
    theme: {
      primary: '#4F46E5',
      onPrimary: '#FFFFFF',
      background: '#FFFFFF',
      surface: '#F9FAFB',
      accent: '#22C55E',
      danger: '#EF4444',
    },
  });
</script>

npm / ESM

npm install @avibra/pulse-web-sdk
import { Pulse } from '@avibra/pulse-web-sdk';

Pulse.init({
  partnerId: 'YOUR_PARTNER_ID',
  userId: currentUser.id,
});

API Reference

Pulse.init(config)

Initializes the SDK. Must be called once before any other method.

Pulse.init({
  partnerId: 'app_abc123',          // Required — from Pulse dashboard
  userId: 'user_xyz',               // Required — your user's unique ID
  apiKey: 'pk_live_...',            // Optional — sent as X-Api-Key on all requests
  mode: 'prod',                     // Optional — 'staging' | 'st2' | 'prod' (default: 'prod')
  baseUrl: 'https://...',           // Optional — override API base URL (takes precedence over mode)
  theme: {                          // Optional — see Theme Customization
    primary: '#4F46E5',
    onPrimary: '#FFFFFF',
    background: '#FFFFFF',
    surface: '#F9FAFB',
    accent: '#22C55E',
    danger: '#EF4444',
    borderRadius: 16,
    fontFamily: "'Inter', sans-serif",
  },
  debug: false,                     // Optional — enable console logging
  pollIntervalMs: 30000,            // Optional — trigger poll interval (default 30s)
  maxOfflineQueueSize: 100,         // Optional — offline buffer size
  retry: {
    maxAttempts: 3,                 // Optional — retry attempts on failure
    baseDelayMs: 500,               // Optional — initial backoff delay
    maxDelayMs: 10000,              // Optional — max backoff cap
  },
});

Environments

| mode | Base URL | |---|---| | prod (default) | https://pulse.avibra.com/api/v1 | | staging | https://pulse.avibra.com/staging/api/v1 | | st2 | https://pulse.avibra.com/st2/api/v1 |

If baseUrl is also provided, it takes precedence over mode.


Pulse.trackEvent(name, properties?)

Track a user action. The SDK sends this to the Pulse backend which evaluates your trigger rules. If a survey is triggered, the widget appears automatically.

// On account close button click:
Pulse.trackEvent('ACCOUNT_CLOSE_CLICKED', {
  plan: 'enterprise',
  tenure_days: 365,
});

// On feature toggle:
Pulse.trackEvent('FEATURE_DISABLED', { feature: 'two-factor-auth' });

Built-in protections:

  • Max 100 events/minute (sliding window rate limit)
  • 500ms debounce per event name (prevents double-fires)
  • Fire-and-forget (never blocks your UI thread)

Pulse.identifyUser(payload)

Associate the current user with profile attributes. Call after login or when user context changes.

Pulse.identifyUser({
  first_name: 'Jane',
  last_name: 'Doe',
  email: '[email protected]',
  attributes: {
    company: 'Acme Corp',
    employee_count: 500,
    region: 'EMEA',
  },
});

Pulse.startVoiceSurvey(signalId)

Programmatically launch a voice survey within the widget. Useful for testing or custom trigger logic.

button.addEventListener('click', async () => {
  await Pulse.startVoiceSurvey('signal_churn_intent');
});

Pulse.startCall(config)

Launch a standalone voice call session — no widget, no trigger polling. Designed for dedicated call pages (e.g. connect-pulse.avibra.com).

Pulse.startCall({
  partnerId: 'app_abc123',   // Required
  userId: 'user_xyz',        // Required
  signalId: 'signal_123',    // Required
  mode: 'prod',              // Optional — 'staging' | 'st2' | 'prod'
  apiKey: 'pk_live_...',     // Optional
  theme: { ... },            // Optional
  debug: false,              // Optional
});

Flow: Shows a loading screen while fetching signal details, then transitions directly to the ringing screen. If the signal fetch fails, an error screen is shown.

Typical usage — reading params from the URL hash:

<script src="https://cdn.avibra.com/sdk/v1/pulse.min.js"></script>
<script>
  const params = new URLSearchParams(location.hash.slice(1));
  Pulse.startCall({
    partnerId: params.get('pid'),
    userId:    params.get('uid'),
    signalId:  params.get('sid'),
    mode:      params.get('m') || 'prod',
  });
</script>

URL format: https://connect-pulse.avibra.com/#pid=abc&uid=xyz&sid=signal_123&m=prod


Pulse.destroy()

Tear down the SDK completely. Removes the widget, stops polling, and clears all intervals.

// On logout, user switch, or SPA route teardown:
Pulse.destroy();

Theme Customization

Pulse uses CSS design tokens inside a Shadow DOM to guarantee zero conflicts with your application styles.

Available Tokens

| Token | CSS Variable | Description | |---|---|---| | primary | --pulse-primary | Brand color for buttons, waveform | | onPrimary | --pulse-on-primary | Text/icon color on primary backgrounds | | background | --pulse-background | Widget outer background | | surface | --pulse-surface | Inner card/surface color | | accent | --pulse-accent | Success states, active indicators | | danger | --pulse-danger | End-call, destructive actions | | borderRadius | --pulse-border-radius | Widget corner rounding (px) | | fontFamily | --pulse-font-family | Font family override |

Example: Dark Theme

Pulse.init({
  partnerId: 'app_123',
  userId: 'user_123',
  theme: {
    primary: '#6366F1',
    onPrimary: '#FFFFFF',
    background: '#1E1E2E',
    surface: '#2D2D44',
    accent: '#10B981',
    danger: '#F43F5E',
    borderRadius: 20,
    fontFamily: "'Inter', sans-serif",
  },
});

Example: Corporate / Neutral Theme

theme: {
  primary: '#0F172A',
  onPrimary: '#FFFFFF',
  background: '#FAFAFA',
  surface: '#F1F5F9',
  accent: '#0EA5E9',
  danger: '#DC2626',
}

Analytics Platform Integration

If you already use Mixpanel, Braze, or Segment, you don't need to call Pulse.trackEvent() at all.

Setup Steps

  1. Configure a webhook in your analytics platform to forward events to:

    POST https://pulse.avibra.com/api/v1/webhooks/segment   # Segment
    POST https://pulse.avibra.com/api/v1/webhooks/mixpanel  # Mixpanel
    POST https://pulse.avibra.com/api/v1/webhooks/braze     # Braze
  2. Include the Pulse SDK in your app (init only, no tracking):

    Pulse.init({ partnerId: 'YOUR_PARTNER_ID', userId: currentUser.id });
    // SDK will poll GET /triggers every 30s and show widget when a trigger fires
  3. Set trigger rules in the Pulse dashboard linking your event names to surveys.


Security Model

API Key Authentication

When apiKey is provided, it is sent on every outbound request as the X-Api-Key header.

Headers attached to every request:

  • X-Pulse-Partner-Id — your application ID
  • X-Pulse-Nonce — random 16-byte hex nonce (replay protection)
  • X-Pulse-Timestamp — ISO 8601 timestamp
  • X-Api-Key — your API key (when apiKey is set)

⚠️ Important: Treat your apiKey as a secret. Avoid hardcoding it in publicly accessible source code. For production web apps, serve it from an authenticated backend endpoint.

Shadow DOM Isolation

The Pulse widget uses attachShadow({ mode: 'closed' }). External JavaScript cannot access the widget's internals, preventing CSS injection or DOM tampering.


Performance

| Metric | Target | How achieved | |---|---|---| | Bundle size (gzipped) | < 50KB | VoiceSessionManager lazy-loaded via import() | | Init time | < 100ms | Synchronous module setup, async API calls | | Main thread blocking | None | All API calls are fire-and-forget | | Offline resilience | 100 events buffered | OfflineQueue with localStorage + reconnect drain |


Backend API Contract

| Method | Endpoint | Description | |---|---|---| | POST | /users/track | Track a user event | | POST | /identify | Update user profile | | GET | /triggers?userId= | Check for pending survey trigger | | POST | /voice/session/start | Start a voice survey session | | POST | /voice/session/audio | Send audio chunk (multipart/form-data) |

All endpoints return:

{ "success": true, "data": { ... } }
// or
{ "success": false, "error": { "code": "UNAUTHORIZED", "message": "..." } }

Events Reference

Listen to Pulse widget events on the document:

document.addEventListener('pulse:prompt-shown', (e) => {
  console.log('Prompt shown for survey:', e.detail.signalId);
});

document.addEventListener('pulse:survey-accepted', (e) => {
  // User clicked "Yes, Let's Go"
  analytics.track('Pulse Survey Accepted', { signalId: e.detail.signalId });
});

document.addEventListener('pulse:survey-declined', (e) => {
  // User clicked "Maybe Later"
});

document.addEventListener('pulse:session-completed', (e) => {
  console.log(`Session completed in ${e.detail.duration}s`);
});

Troubleshooting

Widget not appearing

  • Ensure Pulse.init() completed without errors (debug: true will log all activity).
  • Verify your trigger rules are configured in the Pulse dashboard.
  • Check that your backend is returning hasPendingTrigger: true from GET /triggers.

Microphone not working

  • The browser requires HTTPS for navigator.mediaDevices.getUserMedia.
  • Check that the user has granted microphone permissions in browser settings.
  • Safari requires a user gesture (button click) before requesting mic access.

Events not reaching backend

  • Check the offline queue: if navigator.onLine is false, events are buffered.
  • Enable debug: true and inspect [Pulse][INFO] console logs.
  • Verify partnerId is correct and your backend is reachable at baseUrl.

TypeScript types

All public types are exported from the package:

import type { PulseConfig, ThemeConfig, IdentifyPayload, SurveyTrigger } from '@avibra/pulse-web-sdk';

Local Development

cd sdk
npm install
npm run build:watch   # Rebuild on change
npm test              # Run test suite
npm run lint          # Lint TypeScript

Open examples/basic-integration.html in a browser (after building) to test the widget locally.


License

MIT © Avibra Engineering