Skip to main content
[AVAILABLE], published, @parmana/sdk on npm. Real, public, installable with a plain npm install @parmana/sdk. This page said otherwise (“private, not published to any registry”) until 2026-09-14; that was stale even at v1.1.1, confirmed live against the npm registry while fixing this page. Bearer-key authentication, a real thrown error taxonomy, and full test coverage are all in place, along with corrected model types (see below), client.verify()/client.version(), and client.createTransaction(). v1.1.2, adding createBusinessTransaction() (see below), published 2026-09-14, confirmed via npm view @parmana/sdk version. See the fix history at the bottom of this page.
v1.1.2’s published package metadata (homepage/repository/bugs in npm view @parmana/sdk) still points at this repository’s old name, parmana-exp. The repo was renamed to parmana after that version’s build artifacts were created, and registry metadata is immutable per version. Does not affect installing or using the package; fixed in source (typescript/package.json) for whenever a future version next publishes.
Package name history, for anyone who finds an old reference: renamed from @parmana/legacy-reference, itself renamed from @parmana/typescript-sdk. See Changelog for when. @parmana/sdk is the current, correct, published name.

Install

Real registry install. npm view @parmana/sdk version returns the current published version directly from npm, confirmed while fixing this page’s own stale claim otherwise.

Bearer-key authentication

Configuration.apiKey is sent as Authorization: Bearer <apiKey> on every request, by HttpTransport:
apiKey is optional in Configuration, for the same reason it’s optional server-side: local development against a server started with PARMANA_AUTH_DISABLED=true needs no key. Every real deployment requires one — an omitted or wrong key gets a real 401, thrown as AuthenticationError (see below), not a silent failure. See Authentication for how keys are minted.

Errors, they actually throw now

HttpTransport.send() checks response.status and throws the matching typed error for every non-2xx response, built from the real {error, code?} envelope (packages/api/src/middleware/error-handler.ts):
All extend ParmanaError, which carries a stable code (ErrorCode enum) and optional cause/requestId. 403 (AuthorizationError) is a real status this API returns: an authenticated caller asserting an authority.principalId it isn’t permitted to assert (isPrincipalAllowed, packages/api/src/routes/execute.ts), documented in the Error catalog and the OpenAPI spec.
One route is deliberately exempt from this mapping. POST /policies/validate never uses the shared {error, code?} envelope: every status it returns (200, 400, 404) is {valid, errors}, the caller’s answer, not an SDK-level failure. PolicyApi.validate opts those two statuses out via TransportRequest.nonThrowingStatuses; a 401 on the same route still throws AuthenticationError, since that one is generated by caller-auth middleware before the route’s own handler runs, using the shared envelope like every other route. See Error handling.

Model types, matched to the real schemas

typescript/src/models/*.ts is hand-maintained, unlike the Python SDK’s generated models, and kept in sync field-for-field against the real JSON schemas (schemas/common/*.schema.json):
  • Authority.displayName and Verification.message are optional, matching the real API.
  • Authorization.expiresAt is present (optional).
  • BusinessTransactionMetadata requires only businessTransactionId; every other field (tenantId, correlationId, sourceSystem, submittedBy, submittedAt) is optional, matching the real API.
  • ExecutionTrustRecord includes the required signature field and the optional settlementConfirmations field, with their own SettlementConfirmation/Signature models.
  • Override.approvedBy is the real field name (the real schema has no authorityId on Override), plus the optional justification field. Currently unreachable in practice, no route in this repository creates an Override.
  • Decision.outcome, Execution.status, Execution.mode, and BusinessTransaction.status are literal unions matching the real enums, not bare string.
  • Execution includes completedAt, evidence (where Connector evidence lives), and metadata.

client.verify() and client.version()

  • client.verify(businessTransactionId): POST /verify, runs a fresh verification and appends a new Verification to the record’s history. getLatestVerification() (GET /verification/:id) reads a cached verification; this triggers a fresh one.
  • client.version(): GET /version.

POST /transactions

client.createTransaction(transaction) is a second, independent entry point into the identical execution pipeline as execute() (POST /execute), differing only in its 201 status code. It’s a stable, long-standing route with dedicated test coverage (packages/api/tests/unit/transactions-api.test.ts), not a new or evolving surface.

createBusinessTransaction(), no more hand-synced ids

Added 2026-09-14, ships in v1.1.2, published, available via a plain npm install @parmana/sdk. A BusinessTransaction has three id pairs the server’s own BusinessTransactionValidator cross-checks before policy ever runs: metadata.businessTransactionId must equal businessTransactionId, authorization.authorityId must equal authority.authorityId, intent.authorizationId must equal authorization.authorizationId. Every “X must match Y” 400 documented in the end-to-end Paytm guide came from hand-building a request object and getting one of those wrong. createBusinessTransaction() derives all three automatically:
businessTransactionId defaults to a fresh crypto.randomUUID() if you don’t supply one. A value you do supply is your own responsibility to keep unique per attempt, since it also doubles as the server’s idempotency key. authorityType defaults to "SERVICE" (never "AGENT", which doesn’t exist server-side). See typescript/examples/06-create-business-transaction.ts (the same flow as 02-execute.ts, side by side, for direct comparison) and typescript/test/createBusinessTransaction.test.ts (11 unit tests) / typescript/test/integration/examples.integration.test.ts (proves the generated ids round-trip correctly through a real running server, not just in isolation).

Building from source / contributing

Installing the published package (above) is the right path for using the SDK. To build from source inside this monorepo instead:

Test suite

157 tests across 16 files. Run npx vitest run from the repo root, not typescript/: several tests read PARMANA_POLICY_DIR relative to process.cwd() and fail with confusing PolicyNotFoundError-shaped errors otherwise. Coverage includes: unit tests for every error-mapping case (test/HttpTransport.test.ts, test/Errors.test.ts), unit tests for createBusinessTransaction() (test/createBusinessTransaction.test.ts, 11 cases), unit tests for client.verify()/client.version()/client.createTransaction() (test/NewApiMethods.test.ts), a retry-logic suite (test/RetryPolicy.test.ts, real backoff on idempotent GETs against 502/503/504, POSTs never retried, matching the Python SDK’s own retry behavior), per-route unit suites (test/AuditApi.test.ts, test/HealthApi.test.ts, test/PolicyApi.test.ts, test/RefusalApi.test.ts, test/ReplayApi.test.ts, test/RuntimeApi.test.ts, test/VerificationApi.test.ts), and two integration suites that boot the actual @parmana/api Express application: real StaticKeyAuthenticator, real PolicyEngine, real Ed25519 signing, the real, NODE_ENV=test-only generic test-fixture connector (createTestFixtureConnector.ts, successor to the now-removed vendor-payment), on a real OS-assigned TCP port, driven by the real ParmanaClient over real HTTP: test/integration/parmana-client.integration.test.ts (a real 401, a real 403, a real 400, a real policy rejection, a real 404, a real 409 duplicate, plus version(), verify(), and createTransaction(), each asserted against the exact typed result and exact message the server returned) and test/integration/examples.integration.test.ts (runs the quickstart example script itself, see Changelog).

Errors defined but never thrown

typescript/src/errors/ also declares VerificationError and ReplayError. Neither is thrown anywhere: no verified HTTP condition in this API corresponds to them (POST /verify and POST /replay return their semantic result, VERIFIED/FAILED, or a replay outcome, inside an ordinary 200, not as an error). They remain defined, unused: inventing a throw condition for them would describe behavior the real API doesn’t have.

Next

End-to-end: agent → Parmana → Paytm

The full, no-ambiguity runbook this SDK builds toward: real infrastructure, every error message, every fix.

Python SDK

The other maintained SDK: same bearer-key model, generated (not hand-maintained) models.

Error catalog

Every error the real API returns, independent of any SDK.