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

@pymthouse/builder-sdk

v0.4.1

Published

PymtHouse Builder API and OIDC client (OpenID-certified oauth4webapi)

Downloads

880

Readme

@pymthouse/builder-sdk

Source repository: pymthouse/builder-sdk. The npm package name is @pymthouse/builder-sdk.

TypeScript client for the PymtHouse Builder API, Usage API, and OIDC issuer surfaces.

OAuth/OIDC protocol calls use oauth4webapi (OpenID-certified relying-party implementation). PymtHouse-specific REST paths and helpers live in PmtHouseClient.

Install

pnpm add @pymthouse/builder-sdk

Maintainers: see docs/RELEASING.md for trusted publishing and re-running failed releases.

Quick start

import { PmtHouseClient } from "@pymthouse/builder-sdk";
import {
  createPmtHouseClientFromEnv,
  getPymthouseBaseUrl,
} from "@pymthouse/builder-sdk/env";

const client = createPmtHouseClientFromEnv();
const base = getPymthouseBaseUrl();
const discovery = await client.getDiscovery();

Or construct explicitly:

import { PmtHouseClient } from "@pymthouse/builder-sdk";

const client = new PmtHouseClient({
  issuerUrl: process.env.PYMTHOUSE_ISSUER_URL!,
  publicClientId: process.env.PYMTHOUSE_PUBLIC_CLIENT_ID!,
  m2mClientId: process.env.PYMTHOUSE_M2M_CLIENT_ID!,
  m2mClientSecret: process.env.PYMTHOUSE_M2M_CLIENT_SECRET!,
  allowInsecureHttp: process.env.PYMTHOUSE_ISSUER_URL?.startsWith("http:"),
});

User tokens: short-lived JWT or long-lived signer session

Use mintUserAccessToken() when your backend needs the short-lived Builder-minted user JWT directly:

const userJwt = await client.mintUserAccessToken({
  externalUserId: "naap-user-123",
  scope: "sign:job",
});

Use mintUserSignerSessionToken() when you want the user-facing opaque pmth_... signer session. This first mints the short-lived user JWT, then performs the RFC 8693 token exchange with the confidential M2M client:

const signerSession = await client.mintUserSignerSessionToken({
  externalUserId: "naap-user-123",
  scope: "sign:job",
});

For advanced flows that already have a user JWT, call exchangeForSignerSession({ userJwt }) directly.

Dashboard API keys (long-lived pmth_*)

Create a key in the Dashboard API keys page, then exchange it for a signer session without repeating device login:

const session = await client.exchangeApiKeyForSignerSession({
  apiKey: process.env.PMTH_API_KEY!,
  facadeUrl: process.env.DASHBOARD_ORIGIN!, // e.g. https://dashboard.example.com
  scope: "sign:job",
});
// session.access_token — opaque signer bearer for discovery / gateway

See examples/stream-with-api-key.mjs for a minimal Node script.

Integrators can use the higher-level workflow helpers:

const session = await client.mintSignerSessionForExternalUser({
  externalUserId: "naap-user-123",
  email: "[email protected]",
});
// session.accessToken is opaque pmth_…

await client.approveDeviceLogin({
  externalUserId: "naap-user-123",
  userCode: "ABCD-EFGH",
  publicClientId: process.env.PYMTHOUSE_PUBLIC_CLIENT_ID,
});

Usage API: session-scoped scope=me BFF helper

const payload = await client.fetchUsageForExternalUser({
  externalUserId: "naap-user-123",
  startDate,
  endDate,
});
// payload.currentUser includes fiat totals + merged pipelineModels

App manifest

const { manifest, etag, notModified } = await client.getAppManifest({
  ifNoneMatch: cachedEtag ?? undefined,
});

Remote signer identity webhook

For go-livepeer -remoteSignerWebhookUrl deployments, builder-sdk provides the reference integration security webhook that validates end-user credentials and returns UsageIdentity to the signer (POST /authorize).

Transport (signer shared-secret auth, wire protocol) is separate from end-user auth strategies (EndUserAuthVerifier). OIDC/JWT is the default; an API-key adapter and a composite "first match" adapter are also provided, and you can plug in any custom verifier.

import {
  createApiKeyEndUserVerifier,
  createOidcRemoteSignerWebhookConfig,
  createRemoteSignerAuthorizeHandler,
  type EndUserAuthVerifier,
} from "@pymthouse/builder-sdk/signer/webhook";

// OIDC (default): Auth0, pymthouse issuer, etc.
const authorize = createRemoteSignerAuthorizeHandler(
  createOidcRemoteSignerWebhookConfig({
    webhookSecret: process.env.WEBHOOK_SECRET!,
    jwtIssuer: process.env.JWT_ISSUER!,
    jwtAudience: process.env.JWT_AUDIENCE!,
    claimMapping: { claimClientId: "azp", usageSubjectType: "auth0_user_id" },
  }),
);

// API key: resolve your own keys to a UsageIdentity
const apiKeyVerifier = createApiKeyEndUserVerifier({
  issuer: process.env.JWT_ISSUER!,
  resolveApiKey: async (key) => (await lookup(key)) ?? null,
});

// Custom provider: implement EndUserAuthVerifier
const customConfig = {
  webhookSecret: process.env.WEBHOOK_SECRET!,
  endUserAuth: {
    kind: "custom",
    verify: async ({ authorization, payload, request }) => {
      // validate provider credentials, return UsageIdentity
      return { identity: { ... }, expiry: Math.trunc(Date.now() / 1000) + 300 };
    },
  } satisfies EndUserAuthVerifier,
};

Env vars align with auth0-livepeer bootstrap output (.env.livepeer). For Auth0, set CLAIM_CLIENT_ID=azp and USAGE_SUBJECT_TYPE=auth0_user_id.

Subpath exports

| Import | Purpose | |--------|---------| | @pymthouse/builder-sdk | PmtHouseClient, usage helpers, manifest parsers, token helpers | | @pymthouse/builder-sdk/signer/webhook | Identity webhook for -remoteSignerWebhookUrl | | @pymthouse/builder-sdk/config | isPymthouseConfigured, readPymthouseEnv (Edge/middleware-safe) | | @pymthouse/builder-sdk/tokens | Signer session TTL, JWT shape helpers, parseSignerSessionExchange | | @pymthouse/builder-sdk/format | Wei formatting for Usage API | | @pymthouse/builder-sdk/env | createPmtHouseClientFromEnv, getPymthouseBaseUrl (server-only) | | @pymthouse/builder-sdk/device | RFC 8628 pollDeviceToken | | @pymthouse/builder-sdk/device-initiate | Option B device login validation (Edge-safe) | | @pymthouse/builder-sdk/verify | RFC 9068 verifyJwt |

Usage API: duplicate byUser rows

When getUsage({ groupBy: "user" }) returns multiple byUser rows with the same externalUserId, sum them with summarizeUsageForExternalUser (or aggregateUsageByExternalUserId on byUser alone):

import { summarizeUsageForExternalUser } from "@pymthouse/builder-sdk";

const usage = await client.getUsage({ groupBy: "user", startDate, endDate });
const summary = summarizeUsageForExternalUser(usage, externalUserId);
// summary.requestCount, summary.feeWei (wei string)

Billing: plans, retail usage, signed-ticket ingest

Plans (apiVersion=2): listBillingProducts({ apiVersion: "2" }) returns BillingProduct[] with capability pricing and sync status. syncBillingProduct(planId) POSTs to OpenMeter.

Retail estimates: getUsage({ includeRetail: true, groupBy: "pipeline_model" }) adds endUserBillableUsdMicros / fiat rows when the active plan has retail rates.

Metering: sign directly against the remote signer DMZ with createDirectSignerProxyHandler or forwardDirectSignerRequest. Usage is emitted asynchronously by go-livepeer to Kafka and ingested by the OpenMeter collector. The PymtHouse /api/signer/* HTTP proxy and synchronous HTTP signed-ticket ingest are removed.

Routing: getSignerRouting() returns the remote DMZ URL, webhook URL, and migration hints (directDmz / deprecatedHostedFacade).

Allowances (OpenMeter): Trial and manual USD micros allowance use OpenMeter entitlements — not a Postgres wei ledger.

| Method | SDK | HTTP | |--------|-----|------| | Read balance | getUsageBalance(externalUserId) | GET .../usage/balance?externalUserId= | | Read allowance detail | getUserAllowances(externalUserId) | GET .../users/{id}/allowances | | Top-up grant | grantUserAllowance(externalUserId, { amountUsdMicros, source }) | POST .../users/{id}/allowances |

grantUserCredits / getUserCredits remain as deprecated aliases that call the allowances / balance endpoints. POST .../users/{id}/credits was removed from PymtHouse (the route may still re-export allowances temporarily).

Plan pricing helpers: markupPercentToRetailRateUsd, applyRetailRateToNetworkMicros (exported from the main entry).

Usage API: pipeline/model grouping

When getUsage({ groupBy: "pipeline_model", startDate, endDate, userId }) returns byPipelineModel, use listUsageByPipelineModel for a stable-sorted copy. Pass the optional gatewayRequestId filter to scope results to a single upstream gateway request:

import { listUsageByPipelineModel } from "@pymthouse/builder-sdk";

const usage = await client.getUsage({
  groupBy: "pipeline_model",
  startDate,
  endDate,
  userId: internalUserId,
  gatewayRequestId, // optional: filter to a single gateway request
});
const rows = listUsageByPipelineModel(usage);

Documentation

Authoritative API behavior: PymtHouse docs/builder-api.md.

Server-only: createPmtHouseClientFromEnv / @pymthouse/builder-sdk/env

M2M credentials are confidential. The env entry point:

  1. Throws as soon as the module loads in a browser (detects globalThis.window), so a mistaken client import fails immediately instead of silently bundling secrets.
  2. Does not stop someone from putting m2mClientSecret in new PmtHouseClient({ ... }) in client code—you still must not do that.

Next.js — build-time guard (optional): in a file that is only used from the server, add the official marker so the bundler errors instead of shipping the module to the client:

// e.g. lib/pymthouse-server.ts
import "server-only";

export {
  createPmtHouseClientFromEnv,
  getPymthouseBaseUrl,
} from "@pymthouse/builder-sdk/env";

Import createPmtHouseClientFromEnv only from that wrapper (or from Route Handlers / Server Actions directly).

Next.js (monorepo) consumption

When the SDK lives as a sibling folder (e.g. ../node-pymt-sdk), enable experimental.externalDir in next.config and re-export from a small lib shim that points at ../../node-pymt-sdk (see the website app in this org). Published installs from npm use the package name directly without shims.

License

MIT