Skip to main content
@pymthouse/builder-sdk is the official TypeScript/npm SDK for PymtHouse app builders. It wraps:
  • Confidential Builder and Usage REST APIs (M2M auth, user provisioning, billing)
  • OIDC protocol flows (token mint, exchange, device login, JWT verify) via oauth4webapi
  • Signer integration (direct DMZ proxy and identity webhook for go-livepeer)
  • Client-safe utilities (config checks, device-initiate validation, formatting, plan pricing math)

Install

pnpm add @pymthouse/builder-sdk
# or
npm install @pymthouse/builder-sdk
Node ≥ 20 required.

Quick start

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

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

Explicit construction

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!,
});

Environment variables

The env subpath reads these variables. Set them in your backend environment or secret manager.
VariableValueNotes
PYMTHOUSE_ISSUER_URLhttps://your-pymthouse.example/api/v1/oidcMust match iss in issued tokens.
PYMTHOUSE_PUBLIC_CLIENT_IDapp_…Public OIDC client id. Used in device/browser flows and JWT claims.
PYMTHOUSE_M2M_CLIENT_IDm2m_…Confidential M2M client id. Server-side only.
PYMTHOUSE_M2M_CLIENT_SECRETpmth_cs_…M2M client secret. Server-side only. Rotate via the credentials endpoint.
The @pymthouse/builder-sdk/env subpath throws immediately if imported in a browser context (detects globalThis.window). In Next.js, add import "server-only" in any file that re-exports createPmtHouseClientFromEnv to enforce the server-only constraint at build time.

PmtHouseClient method reference

All REST calls use HTTP Basic auth against {issuerOrigin}/api/v1/apps/{publicClientId}/….

Discovery & OIDC

MethodDescription
getDiscovery()Fetch OIDC discovery document (5-min cache).
verifyIssuer(iss)Validate an issuer URL against this client’s issuer.
issueMachineAccessToken(scope?)Client credentials grant; returns a machine access token.

User management

MethodDescription
listAppUsers()GET .../users — list provisioned users. Requires users:read.
upsertAppUser(input)POST .../users — create or update a user. Requires users:write.
deleteAppUser({ externalUserId })DELETE .../users — deactivate a user.

Token minting & exchange

MethodDescription
mintUserAccessToken(input)POST .../users/{id}/token — short-lived JWT. Requires users:token.
exchangeForSignerSession({ userJwt, resource? })RFC 8693: user JWT → opaque pmth_* signer session.
mintUserSignerSessionToken(input)Mint user JWT + exchange in one call.
mintSignerSessionForExternalUser(input)Upsert user + mint + exchange workflow.
createSignerSessionToken({ userJwt? })Exchange or fall back to machine token.
exchangeApiKeyForUserAccessToken({ apiKey })POST .../auth/api-key/token — API key → short-lived JWT.
exchangeApiKeyForSignerSession({ apiKey, facadeUrl? })API key → opaque signer session.

Device flow

MethodDescription
parseDeviceApprovalRedirect(searchParams)Parse initiate-login redirect parameters.
completeDeviceApproval({ userJwt, userCode })RFC 8693 token exchange to bind a device grant.
approveDeviceLogin(input)Full Option B device approval workflow.

Usage & billing

MethodDescription
getUsage(input?)GET .../usage with groupBy/retail filters.
fetchUsageForExternalUser(input)BFF-style scope=me usage rollup for a single user.
getUsageBalance(externalUserId)GET .../usage/balance — entitlement balance.
getUserAllowances(externalUserId)GET .../users/{id}/allowances.
grantUserAllowance(externalUserId, input)POST .../users/{id}/allowances.
getUserSubscription(externalUserId)GET .../users/{id}/subscription.
listBillingProducts()GET .../plans?apiVersion=2 — returns BillingProduct[].
syncBillingProduct(planId)POST .../plans/{id}/sync — explicit OpenMeter sync.

Signer routing

MethodDescription
getSignerRouting()GET .../signer/routing — DMZ URL, webhook URL, metering mode.

Subpath exports

ImportRuntime safetyPurpose
@pymthouse/builder-sdkNode + EdgePmtHouseClient, usage helpers, manifest parsers, token helpers, error types
@pymthouse/builder-sdk/envNode only (throws in browser)createPmtHouseClientFromEnv, getPymthouseBaseUrl
@pymthouse/builder-sdk/configEdge-safeisPymthouseConfigured, readPymthouseEnv, URL helpers
@pymthouse/builder-sdk/tokensEdge-safeSigner session TTL constants, JWT shape helpers, parseSignerSessionExchange
@pymthouse/builder-sdk/deviceNode + EdgepollDeviceToken — RFC 8628 device code polling
@pymthouse/builder-sdk/device-initiateEdge-safevalidateDeviceInitiateLogin, extractDeviceApprovalFromTargetLink
@pymthouse/builder-sdk/verifyNode + EdgeverifyJwt — RFC 9068 JWT validation via JWKS
@pymthouse/builder-sdk/formatEdge-safeformatWeiToEth, formatWeiToUsd — wei display formatting
@pymthouse/builder-sdk/plan-pricingEdge-safemarkupPercentToRetailRateUsd, applyRetailRateToNetworkMicros, plan pricing math
@pymthouse/builder-sdk/signer/serverNode onlycreateDirectSignerProxyHandler, forwardDirectSignerRequest, createSignerTokenManager
@pymthouse/builder-sdk/signer/webhookNode + EdgecreateRemoteSignerAuthorizeHandler, identity webhook handler + adapters
@pymthouse/builder-sdk/signer/webhook/adapters/oidcNode + EdgeOIDC JWT end-user verifier
@pymthouse/builder-sdk/signer/webhook/adapters/api-keyNode + EdgeAPI key end-user verifier
@pymthouse/builder-sdk/signer/webhook/adapters/trusted-headersEdge-safeTrusted header end-user verifier
@pymthouse/builder-sdk/signer/webhook/adapters/compositeNode + EdgeFirst-match composite verifier

Common usage patterns

Mint a signer session for a user

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

const client = createPmtHouseClientFromEnv();

// Option 1: short-lived JWT only
const userJwt = await client.mintUserAccessToken({
  externalUserId: "naap-user-123",
  scope: "sign:job",
});

// Option 2: full signer session (JWT + RFC 8693 exchange)
const session = await client.mintUserSignerSessionToken({
  externalUserId: "naap-user-123",
  scope: "sign:job",
});
// session.access_token — opaque pmth_… bearer for the DMZ

// Option 3: upsert user + mint + exchange in one call
const session = await client.mintSignerSessionForExternalUser({
  externalUserId: "naap-user-123",
  email: "user@example.com",
});

Check usage balance before signing

const balance = await client.getUsageBalance("naap-user-123");
if (!balance.hasAccess) {
  throw new Error("Insufficient balance");
}

Approve a device login (Option B)

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

Usage BFF rollup

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

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

Next.js server-only guard

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

export {
  createPmtHouseClientFromEnv,
  getPymthouseBaseUrl,
} from "@pymthouse/builder-sdk/env";
Import createPmtHouseClientFromEnv only from this wrapper in Route Handlers or Server Actions.