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

@wtfalch/integrations

v0.2.0

Published

Public client SDK and API types for the Integrations service.

Readme

@wtfalch/integrations

Client SDK for the Integrations service. An organisation connects a Slack or Discord channel, and your app posts messages into it. Requires Node 22+ or a browser with Fetch and AbortSignal.timeout support. The client has no runtime package dependencies.

npm install @wtfalch/integrations
import { ApiError, createIntegrationsClient } from '@wtfalch/integrations';

const integrations = createIntegrationsClient({
  url: serviceOrigin,
  credential: () => serviceKey,
  organisationId,
});

// 1. Send a person to Slack or Discord to pick a channel.
const { url } = await integrations.startInstall({
  registrationId, // your product's Slack or Discord registration
  returnUrl: 'https://app.example.com/settings/integrations',
});

// 2. The service redirects back to returnUrl with ?integration=<connectionId>,
//    or ?integration_error=<code> when the install failed.

// 3. Queue a message. Repeating the same idempotencyKey returns the same message.
const message = await integrations.send({
  connectionId,
  idempotencyKey: `order-${orderId}-paid`,
  title: 'Order paid',
  text: 'Order 1042 was paid.',
  url: 'https://app.example.com/orders/1042',
});

Methods

| Method | Does | |---|---| | createRegistration(input) | Registers a new vendor OAuth app. Returns { id, redirectUri }. Estate operators only; refused for a customer-tenant credential. | | startInstall({ registrationId, returnUrl }) | Returns { url, expiresAt }. The URL is valid for ten minutes. returnUrl's origin must be on the registration's allowlist. | | listConnections() | The organisation's connections. | | getConnection(id) | One connection. state is active, broken or removed. | | deleteConnection(id) | Disconnects the channel and removes the stored webhook. | | checkConnection(id) | Proactively verifies the connection with the vendor, without sending a visible message; marks it broken immediately on a definitive revocation instead of waiting for the next failed send. | | send(input) | Queues a message and returns it with state: 'queued'. | | getMessage(id) | One message. state is queued, sent or failed. | | listMessages(connectionId) | A connection's messages, oldest first. | | retryMessage(id) | Requeues a failed message with a fresh attempt budget, same id and history. |

Messages

text is 1 to 1800 characters. title is at most 200. url is an absolute https URL of at most 500 characters. The service renders them for each vendor and never lets a message ping @channel, @everyone or anyone else.

Delivery is asynchronous. A vendor rate limit delays a message. After eight failed attempts a message is failed; retryMessage requeues it with a fresh attempt budget rather than sending it as a brand-new message. When a customer removes the app or deletes the channel, the connection becomes broken and its queued messages fail; read brokenReason and ask them to connect again.

State callbacks

Give a registration a callbackUrl and a callbackSecret (at least 32 characters) when you create it. The service then POSTs a signed ConnectionStateCallback to that URL each time one of the registration's connections becomes active, broken or removed. It does not follow redirects. A non-2xx answer is retried with backoff, up to eight attempts. One connection's callbacks arrive in order, each at least once, so dedupe on id.

Verify each one against the raw body before trusting it:

import { verifyCallback } from '@wtfalch/integrations';

const event = await verifyCallback({
  secret: process.env.INTEGRATIONS_CALLBACK_SECRET,
  body: await request.text(),
  signature: request.headers.get('x-integrations-signature'),
});

verifyCallback throws on a bad signature, or on a timestamp more than five minutes away.

Errors

API failures throw ApiError with status and code. 409 connection_unavailable means the connection is not active. 409 idempotency_conflict means the key was used with a different body. A network failure or a 5xx is retried up to twice more with backoff before it throws ApiError with status: 0, code: 'network_error' (network) or the upstream status (persistent 5xx). A 4xx is never retried.

Credentials

Use the deployed service's HTTPS origin and a scoped service credential. The credential callback runs for every request, so rotation does not need a new client. The client refuses redirects and omits browser cookies. An HTTP origin is accepted only for localhost development. Do not put a service credential in browser code.

The service implementation is in a private repository. This package holds only the client and its types.