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

@otp-router/client

v0.3.0

Published

Typed backend HTTP client for self-hosted OTP Router

Readme

OTP Router client

A typed Promise client for the self-hosted OTP Router HTTP API. Requires Node.js 26.12.1 or newer. ESM only. Pin the version and review release compatibility and upgrade notes before upgrading.

npm install --save-exact @otp-router/[email protected]
import { randomUUID } from "node:crypto";
import { createClient, OtpRouterApiError } from "@otp-router/client";

const bearerToken = process.env.OTP_ROUTER_API_KEY;
if (bearerToken === undefined) throw new Error("Set OTP_ROUTER_API_KEY");
const client = createClient({
  baseUrl: "https://router.example",
  projectId: "demo",
  bearerToken,
});

const input = {
  recipient: { type: "phone" as const, phoneNumber: "+998901234567" },
  purpose: "login",
  contextId: "authorized-session-id",
  integrationReference: "Flow.AbC_09:attempt-2",
  policyId: "login",
};
const idempotencyKey = randomUUID(); // Persist this key with the input before sending.
const result = await client.createChallenge(input, { idempotencyKey });
// result.data is validated. result.status and result.replayed describe the HTTP response.
const current = await client.getChallenge(result.data.challengeId);

Run this only on a trusted backend. Keep credentials and codes out of client applications and logs. Creation acknowledges durable work; provider acceptance does not verify a person.

Use createClient() for managed challenges, external-code delivery, and history; use createAdminClient() for project and runtime administration. Version 0.3.0 pairs with server 0.3.0; follow the release upgrade instructions when upgrading. Exported TypeScript declarations define methods and DTO fields.

The optional integrationReference correlates upstream flows within a project. Supply opaque identifiers, never personal data or secrets; authorization services and webhook consumers receive it. Creation/preparation save it immutably, and retries must preserve its original presence and exact value. See the correlation contract and coordinate updates for strict authorization/event validators.

Requests and responses are validated at runtime. Results include response data and HTTP metadata; unavailable header metadata is null.

Every mutation requires an explicit idempotencyKey. There are no automatic retries. After an ambiguous failure, retain the original payload/key and reconcile or retry with that same pair according to the HTTP contract. Replays can contain older snapshots.

OtpRouterApiError contains a validated error and HTTP metadata. Branch on error.code, not messages:

try {
  await client.verifyChallenge(
    current.data.challengeId,
    {
      code: "000123",
      purpose: "login",
      contextId: "authorized-session-id",
    },
    { idempotencyKey: randomUUID() },
  );
} catch (error: unknown) {
  if (error instanceof OtpRouterApiError && error.error.code === "incorrect_code") {
    const locked = error.error.reason === "locked";
    // Update your flow; a locked challenge cannot accept more guesses.
  } else {
    throw error;
  }
}

OtpRouterClientError distinguishes configuration, invalid requests, invalid responses, transport failures, timeout, caller abort, and unexpected defects through kind and its error DTO. Raw bodies, credentials, and underlying network errors are not attached. A transport failure or timeout does not establish that a mutation failed on the server.

The default deadline is 30 seconds for the complete request/response. Set timeoutMs on the client or per request in positive integer milliseconds. Pass an AbortSignal to cancel; an already-aborted signal prevents the request. Cancellation does not undo server work. Base URLs may include a reverse-proxy path prefix; redirects are rejected. Supply fetch to use a custom transport.

See the client integration guide, HTTP contract, and history contract.

For administration, construct createAdminClient({ baseUrl, bearerToken: administratorToken }). Project creation requires idempotencyKey; subsequent settings, lifecycle, and grant changes also require etag, taken from a project read or mutation response. Retry lost responses with the same payload, key, and ETag. Administrator and backend credentials are separate. See the runnable administration example.