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.
pnpm add @pymthouse/builder-sdkMaintainers: see docs/RELEASING.md for trusted publishing and re-running failed releases.
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:"),
});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.
Integrators can use the higher-level workflow helpers:
const session = await client.mintSignerSessionForExternalUser({
externalUserId: "naap-user-123",
email: "user@example.com",
});
// session.accessToken is opaque pmth_…
await client.approveDeviceLogin({
externalUserId: "naap-user-123",
userCode: "ABCD-EFGH",
publicClientId: process.env.PYMTHOUSE_PUBLIC_CLIENT_ID,
});const payload = await client.fetchUsageForExternalUser({
externalUserId: "naap-user-123",
startDate,
endDate,
});
// payload.currentUser includes fiat totals + merged pipelineModelsconst { manifest, etag, notModified } = await client.getAppManifest({
ifNoneMatch: cachedEtag ?? undefined,
});| Import | Purpose |
|---|---|
@pymthouse/builder-sdk |
PmtHouseClient, usage helpers, manifest parsers, token helpers |
@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 |
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)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);Authoritative API behavior: PymtHouse docs/builder-api.md.
M2M credentials are confidential. The env entry point:
- 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. - Does not stop someone from putting
m2mClientSecretinnew 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).
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.
MIT