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

@vibefollow/sdk

v1.2.1

Published

Official TypeScript SDK for Vibefollow — identify users, emit the 10 canonical lifecycle events with typed helpers, bulk-backfill existing data, batch events in-memory, and verify webhook signatures. Includes typed error classes, automatic retries with ex

Downloads

178

Readme

@vibefollow/sdk

Official TypeScript SDK for Vibefollow — AI follow-ups that convert SaaS signups. Identify users, emit lifecycle events, and verify outbound webhooks with a single, fully-typed client.

Install

npm install @vibefollow/sdk
pnpm add @vibefollow/sdk
yarn add @vibefollow/sdk

Requires Node 20+. Works in Cloudflare Workers (with nodejs_compat), Vercel Edge, and Deno. The browser is not a supported runtime — your API key must stay server-side.

Quickstart (30 seconds)

import { VibeFollow } from '@vibefollow/sdk';

const vf = new VibeFollow({ apiKey: process.env.VIBEFOLLOW_API_KEY! });

// Upsert a user
await vf.users.identify('usr_42', {
  email: '[email protected]',
  name: 'Jane Doe',
  plan: 'trial',
});

// Emit a lifecycle event
await vf.users.signedUp('usr_42', { plan: 'trial', source: 'organic' });

That's it. The SDK takes care of authentication, idempotency, retries, and error mapping.

Authentication

API keys are issued in the Vibefollow dashboard under Settings → Developers → API keys. They follow the Stripe-style prefix scheme:

  • sk_live_… — production
  • sk_test_… — sandbox (future)

Pass the key once when constructing the client:

const vf = new VibeFollow({ apiKey: process.env.VIBEFOLLOW_API_KEY! });

All requests go out over HTTPS. The key is sent in the Authorization: Bearer … header — never as a query parameter.

Lifecycle events (typed helpers)

The 10 canonical lifecycle events have first-class TypeScript helpers. Use them over events.track() whenever possible — you get autocomplete and protection against name typos.

| Helper | Event name | | --------------------------------------- | ------------------------- | | vf.users.signedUp(userId, props) | user_signed_up | | vf.users.trialStarted(...) | trial_started | | vf.users.featureUsed(...) | feature_used | | vf.users.onboardingStep(...) | onboarding_step | | vf.users.subscriptionChanged(...) | subscription_changed | | vf.users.subscriptionCancelled(...) | subscription_cancelled | | vf.users.paymentFailed(...) | payment_failed | | vf.users.trialExpiring(...) | trial_expiring | | vf.users.userInvited(...) | user_invited | | vf.users.usageThresholdReached(...) | usage_threshold_reached |

Example:

await vf.users.subscriptionChanged('usr_42', {
  from: 'trial',
  to: 'pro',
  interval: 'yearly',
  mrr: 9900, // cents
});

Generic tracking

For everything outside the canonical 9, use events.track():

await vf.events.track('dashboard_created', 'usr_42', {
  source: 'wizard',
  workspaceId: 'ws_3',
});

Custom event names are accepted as-is. The API validates them server-side.

Batch tracking

For high-volume clients (e.g. backfilling historical events), use events.batch() to amortize HTTP overhead:

const batch = vf.events.batch({ maxSize: 100, maxAgeMs: 5_000 });

for (const row of historicalEvents) {
  batch.track(row.name, row.userId, row.properties);
}

// Drain on shutdown.
await batch.flush();

The batch auto-flushes when the queue reaches maxSize OR when maxAgeMs has elapsed since the first queued event. Call flush() explicitly to force a drain (e.g. before process exit).

Webhooks

Vibefollow sends signed webhook deliveries when emails are opened, clicked, bounced, replied to, or when users unsubscribe. Verify each delivery with webhooks.constructEvent():

import { VibeFollow, WebhookSignatureError } from '@vibefollow/sdk';

const vf = new VibeFollow({ apiKey: process.env.VIBEFOLLOW_API_KEY! });

app.post('/webhooks/vibefollow', express.raw({ type: '*/*' }), (req, res) => {
  try {
    const event = vf.webhooks.constructEvent(
      req.body, // raw Buffer or string — NOT JSON.parse(body)
      req.headers['x-vibefollow-signature'],
      process.env.VIBEFOLLOW_WEBHOOK_SECRET!,
    );

    switch (event.type) {
      case 'email.opened':
        console.log('opened', event.data.draftId);
        break;
      case 'email.replied':
        console.log('reply tone:', event.data.tone);
        break;
      // …
    }

    res.sendStatus(204);
  } catch (err) {
    if (err instanceof WebhookSignatureError) return res.sendStatus(401);
    throw err;
  }
});

Why the raw body?

The signature is computed over the exact bytes the server sent. If you parse the body first and re-serialize, whitespace and key ordering changes will fail verification. Always feed the SDK the unmodified request body.

Signature header

X-Vibefollow-Signature: t=<unix_seconds>,v1=<hex_sha256>

The SDK rejects any delivery whose t is outside ±5 minutes (configurable via toleranceSeconds). This is the standard Stripe-style replay window.

Errors

Every error thrown by the SDK extends VibeFollowError. Narrow to a subclass for typed recovery.

| Class | HTTP status | Retryable | Meaning | | ----------------------- | ----------- | ------------ | ------------------------------------------------------ | | AuthError | 401 / 403 | No | API key missing, invalid, or revoked | | ValidationError | 422 | No | Request body failed validation; .field indicates | | RateLimitError | 429 | Yes (auto) | Rate limited; .retryAfterMs from Retry-After | | ServerError | 5xx | Yes (auto) | Server problem; retried with exponential backoff | | NetworkError | 0 | Yes (auto) | DNS, refused, abort, timeout | | WebhookSignatureError | 0 | No | HMAC mismatch or timestamp outside tolerance | | VibeFollowError | other 4xx | No | Generic — base class for everything above |

import { AuthError, RateLimitError } from '@vibefollow/sdk';

try {
  await vf.users.signedUp('usr_42');
} catch (err) {
  if (err instanceof AuthError) {
    console.error('check your API key');
  } else if (err instanceof RateLimitError) {
    console.error('still rate limited after retries; backoff', err.retryAfterMs);
  } else {
    throw err;
  }
}

Auto-retries (with exponential backoff + jitter) are applied on NetworkError, ServerError, and RateLimitError. Non-retryable errors throw on the first failure.

Configuration

new VibeFollow({
  apiKey: process.env.VIBEFOLLOW_API_KEY!,
  baseUrl: 'https://api.vibefollow.com', // default — override for self-host or test
  timeoutMs: 10_000,                     // per-request timeout via AbortController
  maxRetries: 2,                         // up to 3 total attempts on retryable errors
  fetchImpl: globalThis.fetch,           // inject in tests or on edge runtimes
});

Every request automatically includes:

  • Authorization: Bearer <apiKey>
  • Content-Type: application/json
  • User-Agent: vibefollow-sdk/<version>
  • Idempotency-Key: <auto-uuid> (server dedupes within a 24h window)

Edge runtimes

The core (HTTP client + resource APIs) works anywhere fetch, AbortController, and crypto.randomUUID are available — that is, Node 20+, Cloudflare Workers, Vercel Edge, and Deno.

Webhook verification uses node:crypto. On Cloudflare Workers, enable the nodejs_compat flag in wrangler.toml. On pure-edge runtimes without Node compatibility, webhook verification is unsupported in v1; track this issue for a Web Crypto fallback.

Versioning

Semver. Major = breaking. The SDK version is exported as SDK_VERSION and flows into the User-Agent header on every request so the backend can flag outdated clients.

Pre-release dist-tags:

npm install @vibefollow/sdk@beta

See CHANGELOG.md for release notes.

License

MIT