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

@trysaperly/sdk

v3.0.0

Published

TypeScript SDK for the Saperly v2 API — autogenerated from the OpenAPI contract.

Readme

@trysaperly/sdk

TypeScript SDK for the Saperly v2 API — the camelCase data plane (numbers, connections, messaging, voice, usage, consent, pricing, keys).

Autogenerated from the OpenAPI contract. This is the current Saperly SDK — the legacy snake_case /api/v1 surface has been retired.

Install

npm install @trysaperly/sdk

Requires Node.js 18+ (uses global fetch).

Usage

import { configure, numbers, voice } from '@trysaperly/sdk'

configure({ apiKey: process.env.SAPERLY_API_KEY! })

// Provision a number and place a call.
const { data: provisioned } = await numbers.provision({ body: { areaCode: '415' } })
const { data: call } = await voice.place({
  body: { fromNumberId: provisioned!.id, to: '+14155550123' }
})

Every method returns { data, error, request, response }data on success, error (the typed error body) on a non-2xx status. Nothing throws by default; pass { throwOnError: true } to opt into exceptions.

Multiple keys

configure() sets one shared client. To talk to several workspaces from one process, create isolated clients and pass them per call:

import { createSaperlyClient, numbers } from '@trysaperly/sdk'

const client = createSaperlyClient({ apiKey: 'sap_sk_live_...' })
const { data } = await numbers.list({ client })

Resources

numbers, connections, messaging, voice, usage, consent, pricing, keys, assistant, health. Request/response and typed-error types are exported from the package root (e.g. NumbersProvisionData).

Retries

configure() / createSaperlyClient() retry idempotent requests (GET/HEAD/OPTIONS/DELETE) once on 5xx + network errors. POST/PATCH are never retried. Tune or disable with retries:

configure({ apiKey, retries: 3 }) // or retries: 0 to disable

Webhooks

Verify the signature on inbound Saperly webhooks. Saperly signs each delivery x-saperly-signature: v1=<hex> (HMAC-SHA256 over ${timestamp}.${rawBody}) with x-saperly-timestamp; verifyWebhook checks the signature constant-time, then the timestamp window (default 5 min).

import { verifyWebhook } from '@trysaperly/sdk'

const raw = await req.text() // the EXACT bytes — do not re-serialize
const result = await verifyWebhook(raw, process.env.SAPERLY_WEBHOOK_SECRET!, req.headers)
if (!result.valid) return new Response(`invalid: ${result.reason}`, { status: 400 })
// Dedup result.deliveryId for ≥ the tolerance window to defeat replays.

Agent brain (manual mode)

In manual mode a connection's "brain" drives a live phone call: Saperly POSTs a signed { event } to the connection's webhook per call event and reads back exactly ONE directive. @trysaperly/sdk/agent turns that into a typed handler surface — you write per-event handlers, the framework owns signature verification (401 on a bad signature), parsing (400 on a malformed event), dispatch, and the fail-safe rule (past the signature gate it always returns 200 with a graceful directive, since a 5xx degrades a live call). The /agent entry is dependency-light (Web Crypto + plain TS, no generated client, no effect), so it imports cleanly in Node 18+, Bun, Deno (npm:@trysaperly/sdk/agent), and Cloudflare Workers.

import { agentBrain } from '@trysaperly/sdk/agent'

// Mount on any Web-`Request` runtime (Workers, Deno, Bun.serve, Next.js, Hono…).
export default {
  fetch: agentBrain({
    secret: env.SAPERLY_MANUAL_SECRET, // the connection's manualSecret
    onInboundCall: ({ event }) => `Hi, you've reached ${event.to}. How can I help?`,
    onTurn: ({ event }) => `You said: ${event.userText}`, // a string → speak(...)
    onCallEnded: () => {} // terminal — the directive is ignored
  })
}

A handler returns a Directive (speak, reject, transfer, …, built with the exported builders), a string (shorthand for speak), or nothing (a safe default). createAgentBrain({ secret }).on('turn', …) is the same engine with a chainable API.

Auth

Scoped Saperly API key (sap_sk_live_…), sent as Authorization: Bearer <key>. Mint keys in the dashboard.

Development

This package is generated — do not edit src/generated/. The only hand-written file is src/index.ts (the configure() facade + re-exports).

bun install
bun run generate   # re-emit ../openapi.v2.json + regenerate src/generated
bun run build      # bundle to dist/ (tsdown → ESM + d.ts)
bun test           # smoke tests (auth + base-URL wiring)

The generator is @hey-api/openapi-ts; config in openapi-ts.config.ts. The contract source of truth is the SaperlyApi Effect HttpApi in packages/api.