TypeScript client for the PymtHouse Builder API, Usage API, and OIDC issuer. Wraps M2M auth, user provisioning, token minting, signer integration, and billing in a single typed package.
@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)
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.
Variable
Value
Notes
PYMTHOUSE_ISSUER_URL
https://pymthouse.com/api/v1/oidc
Must match iss in issued tokens.
PYMTHOUSE_PUBLIC_CLIENT_ID
app_…
Public OIDC client id. Used in device/browser flows and JWT claims.
PYMTHOUSE_M2M_CLIENT_ID
m2m_…
Confidential M2M client id. Server-side only.
PYMTHOUSE_M2M_CLIENT_SECRET
pmth_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
Method
Description
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
Method
Description
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
Method
Description
mintUserAccessToken(input)
POST .../users/{id}/token — short-lived JWT. scope defaults to sign:job. Requires users:token. Throws PmtHouseError({ status: 404, code: "not_found" }) if the user has not been provisioned.
ensureUserAndMintToken(input)
Same as mintUserAccessToken but auto-provisions the user on 404 / not_found via upsertAppUser + retry. Use this for lazy provisioning instead of calling the two methods manually.
exchangeForSignerSession({ userJwt, resource? })
RFC 8693: user JWT → opaque pmth_* signer session.
mintUserSignerSessionToken(input)
Mint user JWT + exchange in one call.
mintSignerSessionForExternalUser(input)
Eager upsert + mint + exchange in one call. Always upserts the user first; use ensureUserAndMintToken when you only need a JWT without the signer exchange.
createSignerSessionToken({ userJwt? })
Exchange or fall back to machine token.
exchangeApiKeyForUserAccessToken({ apiKey })
POST .../auth/api-key/token — API key → short-lived JWT.
createBalanceGate — live balance check inside the identity webhook
Common usage patterns
Construct the client
// Explicit constructor (recommended — handles all env vars including PYMTHOUSE_ALLOW_INSECURE_HTTP)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_ALLOW_INSECURE_HTTP === "1",});// Convenience factory — reads the same env vars automatically.// allowInsecureHttp is inferred from http: issuer URL or PYMTHOUSE_ALLOW_INSECURE_HTTP=1.import { createPmtHouseClientFromEnv } from "@pymthouse/builder-sdk/env";const client = createPmtHouseClientFromEnv();
Mint a user JWT (lazy provisioning)
// ensureUserAndMintToken — auto-provisions on 404, no eager upsert neededconst { access_token } = await client.ensureUserAndMintToken({ externalUserId: "user-123", // scope defaults to "sign:job" when omitted});// mintUserAccessToken — throws PmtHouseError({ status: 404, code: "not_found" })// if the user hasn't been provisioned yet. Use when you provision eagerly on sign-up.const { access_token } = await client.mintUserAccessToken({ externalUserId: "user-123",});
Mint a signer session for the DMZ
// mintSignerSessionForExternalUser — eager upsert + mint + RFC 8693 exchange in one call// Returns SignerSessionToken (opaque pmth_* bearer)const session = await client.mintSignerSessionForExternalUser({ externalUserId: "user-123",});// session.access_token — opaque pmth_… bearer for the remote signer DMZ// mintUserSignerToken (from signer/server) — M2M client_credentials mint returning// CachedSignerToken with jwt, balanceUsdMicros, lifetimeGrantedUsdMicros, expiresAt.// Use createSignerTokenManager for TTL caching across requests (recommended for production).import { mintUserSignerToken, createSignerTokenManager } from "@pymthouse/builder-sdk/signer/server";