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

@rechannel/sdk

v0.1.0

Published

Node.js SDK for the reChannel API

Readme

@rechannel/sdk

Node.js SDK for the reChannel API. Zero dependencies, uses native fetch (Node 18+), full TypeScript types.

Installation

npm install @rechannel/sdk

Quick Start

import { ReChannel } from '@rechannel/sdk';

// Authenticate with token or API key
const rc = new ReChannel({
  token: 'your-jwt-token',   // or apiKey: 'your-api-key'
  tenantId: 'your-tenant-id',
});

// Or login to get an authenticated client
const { client, auth } = await ReChannel.login({
  email: '[email protected]',
  password: 'password',
});

// Register a new developer account
const { client: newClient, auth: newAuth } = await ReChannel.register({
  email: '[email protected]',
  password: 'password',
  companyName: 'Acme Inc',
});

Configuration

| Option | Type | Required | Default | | ---------- | -------- | -------- | ---------------------------------- | | token | string | * | - | | apiKey | string | * | - | | tenantId | string | yes | - | | baseUrl | string | no | https://rc-api.rift.wyrld.com |

* Either token or apiKey must be provided.

You can update credentials at runtime:

rc.setToken('new-token');
rc.setTenantId('new-tenant-id');

Resources

Users

CRUD user profiles, manage device tokens, and list user events.

// Create or update a user
const user = await rc.users.upsert({
  externalId: 'user-123',
  email: '[email protected]',
  firstName: 'Alice',
  customAttributes: { plan: 'pro' },
});

// Bulk upsert (up to 100)
const users = await rc.users.bulkUpsert([
  { externalId: 'user-1', email: '[email protected]' },
  { externalId: 'user-2', email: '[email protected]' },
]);

// Get, update, delete
const user = await rc.users.get('user-123');
await rc.users.update('user-123', { firstName: 'Bob' });
await rc.users.delete('user-123');

// List with cursor pagination
const page = await rc.users.list({ limit: 50 });
// => { data: UserProfile[], nextCursor?, hasMore, total? }

// Async iteration over all users
for await (const user of rc.users.listAll()) {
  console.log(user.externalId);
}

// Device tokens
await rc.users.registerDeviceToken('user-123', {
  token: 'fcm-token',
  platform: 'fcm',
});
await rc.users.removeDeviceToken('user-123', 'fcm-token');

// User events
const events = await rc.users.listEvents('user-123');

Events

Track user events individually or in batches.

// Track a single event
const event = await rc.events.track({
  externalUserId: 'user-123',
  eventName: 'purchase_completed',
  properties: { amount: 49.99, currency: 'USD' },
});

// Batch track (max 500 per call)
await rc.events.trackBatch([
  { externalUserId: 'user-1', eventName: 'page_view', properties: { page: '/home' } },
  { externalUserId: 'user-2', eventName: 'page_view', properties: { page: '/pricing' } },
]);

Segments

Create audience segments using DSL queries, manage membership manually or dynamically.

// Create a segment with a DSL query
const segment = await rc.segments.create({
  name: 'Pro Users',
  description: 'Users on the pro plan',
  query: '{"customAttributes.plan": "pro"}',
});

// CRUD
const segments = await rc.segments.list();
const seg = await rc.segments.get(segment._id);
await rc.segments.update(segment._id, { name: 'Premium Users' });
await rc.segments.delete(segment._id);

// Preview a query without saving
const matchingUsers = await rc.segments.preview('{"locale": "en-US"}');

// Evaluate and count
const evaluated = await rc.segments.evaluate(segment._id);
const { count } = await rc.segments.count(segment._id);

// Manual membership
await rc.segments.addMember(segment._id, 'user-123');
await rc.segments.removeMember(segment._id, 'user-123');
const members = await rc.segments.listMembers(segment._id);

Campaigns

Create and manage messaging campaigns. Types: one-shot, scheduled, recurring, event-triggered.

// Create a scheduled push campaign
const campaign = await rc.campaigns.create({
  name: 'Welcome Push',
  type: 'scheduled',
  segmentId: 'seg-id',
  channels: ['push'],
  pushMessage: {
    title: 'Welcome!',
    body: 'Thanks for signing up.',
  },
  scheduledAt: '2026-05-01T12:00:00Z',
});

// Create an event-triggered email campaign
const triggered = await rc.campaigns.create({
  name: 'Cart Abandonment',
  type: 'event-triggered',
  segmentId: 'seg-id',
  channels: ['email'],
  triggerEventName: 'cart_abandoned',
  triggerDelayMs: 3600000, // 1 hour
  emailMessage: {
    from: '[email protected]',
    subject: 'You left something behind',
    html: '<h1>Come back!</h1>',
  },
});

// Lifecycle
await rc.campaigns.activate(campaign._id);
await rc.campaigns.pause(campaign._id);
await rc.campaigns.archive(campaign._id);

// List with filters
const active = await rc.campaigns.list({ status: 'active', type: 'recurring' });

// Async iteration
for await (const c of rc.campaigns.listAll()) {
  console.log(c.name, c.status);
}

// Execution history
const executions = await rc.campaigns.listExecutions(campaign._id);

Journeys

Build multi-step user journeys with delays, conditions, and messages.

// Create a journey
const journey = await rc.journeys.create({
  name: 'Onboarding Flow',
  entryType: 'event',
  triggerEventName: 'user_signed_up',
  nodes: [
    { nodeId: 'delay-1', type: 'delay', config: { delayMs: 86400000 } },
    { nodeId: 'push-1', type: 'push', config: { title: 'Day 1 tip' }, nextNodeId: 'delay-1' },
  ],
});

// Lifecycle
await rc.journeys.activate(journey._id);
await rc.journeys.pause(journey._id);
await rc.journeys.archive(journey._id);

// List and iterate
const journeys = await rc.journeys.list({ status: 'active' });
for await (const j of rc.journeys.listAll()) {
  console.log(j.name);
}

// Enroll a user manually (for entryType: 'api')
const enrollment = await rc.journeys.enroll(journey._id, 'user-123');

// Enrollment management
const enrollments = await rc.journeys.listEnrollments(journey._id);
const single = await rc.journeys.getEnrollment(journey._id, enrollment._id);

Push

Send push notifications, configure FCM, and manage delivery settings.

// Send to a user by externalId
const notification = await rc.push.send({
  externalId: 'user-123',
  title: 'New message',
  body: 'You have a new message.',
  data: { deepLink: '/messages/456' },
});

// Send to a specific device token
await rc.push.sendRaw({
  deviceToken: 'fcm-device-token',
  platform: 'fcm',
  title: 'Hello',
});

// Get and list notifications
const notif = await rc.push.get(notification._id);
const list = await rc.push.list({ externalUserId: 'user-123' });

// FCM configuration
await rc.push.setConfig({
  projectId: 'my-project',
  clientEmail: '[email protected]',
  privateKey: '-----BEGIN PRIVATE KEY-----\n...',
});
const config = await rc.push.getConfig();
await rc.push.deleteConfig();

// Frequency cap (rate limiting)
await rc.push.setFrequencyCap({
  maxMessages: 5,
  windowSeconds: 86400,
  channels: ['push'],
});
const cap = await rc.push.getFrequencyCap();

// Quiet hours (suppress notifications during certain times)
await rc.push.setQuietHours({
  enabled: true,
  startTime: '22:00',
  endTime: '08:00',
  defaultTimezone: 'America/New_York',
});
const quiet = await rc.push.getQuietHours();

Social

Connect social accounts via OAuth, manage integrations, create and publish posts, and fetch insights.

// List available providers
const providers = await rc.social.listProviders();

// Start OAuth flow
const session = await rc.social.createAuthSession({
  provider: 'instagram',
  mode: 'connect',
  redirectUri: 'https://app.example.com/callback',
});
// Redirect user to session.authorizationUrl

// Poll for session completion
const completed = await rc.social.getAuthSession(session._id);

// Select accounts (multi-account OAuth flows)
await rc.social.selectAccounts(session._id, {
  providerAccountIds: ['acct-1', 'acct-2'],
});

// Manage integrations
const integrations = await rc.social.listIntegrations({ provider: 'instagram' });
const integration = await rc.social.getIntegration('integration-id');
await rc.social.updateIntegration('integration-id', { displayName: 'My Brand' });
await rc.social.refreshIntegration('integration-id');
await rc.social.disconnectIntegration('integration-id');

// Fetch provider-specific resources
const pages = await rc.social.getResource('integration-id', 'pages');

// Posts
const post = await rc.social.createPost({
  integrationId: 'integration-id',
  text: 'Hello world!',
  scheduledAt: '2026-05-01T12:00:00Z',
});
const posts = await rc.social.listPosts({ integrationId: 'integration-id' });
const fetched = await rc.social.getPost(post._id);
await rc.social.publishPost(post._id);

// Insights
const accountInsights = await rc.social.getAccountInsights('integration-id', {
  since: '2026-01-01',
  until: '2026-04-01',
});
const postInsights = await rc.social.getPostInsights(post._id);

Email

Connect email domains, verify DNS, and send transactional emails.

// Connect a domain
const email = await rc.email.connect({ domain: 'mail.example.com' });

// Get DNS records and verify
const records = await rc.email.getDnsRecords(email._id);
// Add records to your DNS provider, then verify:
const verified = await rc.email.verify(email._id);

// Send an email
const message = await rc.email.send(email._id, {
  from: '[email protected]',
  to: ['[email protected]'],
  subject: 'Welcome',
  html: '<h1>Welcome!</h1>',
});

// List integrations and messages
const integrations = await rc.email.list();
const messages = await rc.email.listMessages(email._id);

// Delete an integration
await rc.email.delete(email._id);

Analytics

Retrieve delivery and engagement metrics for campaigns, journeys, and users.

// Campaign analytics
const stats = await rc.analytics.campaign('campaign-id');
// => { campaignId, totals: { sent, delivered, opened, ... }, byChannel: { push: {...}, email: {...} } }

// Journey analytics
const journeyStats = await rc.analytics.journey('journey-id');

// Per-node analytics
const nodeStats = await rc.analytics.journeyNodes('journey-id');
// => [{ nodeId, metrics: { entered, exited, ... } }, ...]

// User message history
const userStats = await rc.analytics.user('user-123');
// => { data: [{ type, channel, sourceId, sourceType, status, timestamp }, ...] }

Pagination

All list endpoints return a PaginatedResponse<T>:

interface PaginatedResponse<T> {
  data: T[];
  nextCursor?: string;
  hasMore: boolean;
  total?: number;
}

Fetch the next page by passing the cursor:

const page1 = await rc.users.list({ limit: 50 });
if (page1.hasMore) {
  const page2 = await rc.users.list({ limit: 50, cursor: page1.nextCursor });
}

For automatic pagination, use listAll (available on users, campaigns, and journeys):

for await (const user of rc.users.listAll({ limit: 100 })) {
  // processes all users across all pages
}

Error Handling

All errors extend ReChannelError. Specific subclasses map to HTTP status codes:

| Class | Status | Properties | | --------------------- | ------ | ----------------------- | | ApiError | any | status, details | | AuthenticationError | 401 | - | | NotFoundError | 404 | - | | ValidationError | 400 | details | | RateLimitError | 429 | retryAfter (seconds) |

import { ApiError, NotFoundError, RateLimitError } from '@rechannel/sdk';

try {
  await rc.users.get('nonexistent');
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log('User not found');
  } else if (err instanceof RateLimitError) {
    console.log(`Retry after ${err.retryAfter}s`);
  } else if (err instanceof ApiError) {
    console.log(`API error ${err.status}: ${err.message}`);
  }
}

Requirements

  • Node.js >= 18.0.0 (uses native fetch)
  • TypeScript >= 5.0 (recommended)