[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
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.displayNameandVerification.messageare optional, matching the real API.Authorization.expiresAtis present (optional).BusinessTransactionMetadatarequires onlybusinessTransactionId; every other field (tenantId,correlationId,sourceSystem,submittedBy,submittedAt) is optional, matching the real API.ExecutionTrustRecordincludes the requiredsignaturefield and the optionalsettlementConfirmationsfield, with their ownSettlementConfirmation/Signaturemodels.Override.approvedByis the real field name (the real schema has noauthorityIdonOverride), plus the optionaljustificationfield. Currently unreachable in practice, no route in this repository creates anOverride.Decision.outcome,Execution.status,Execution.mode, andBusinessTransaction.statusare literal unions matching the real enums, not barestring.ExecutionincludescompletedAt,evidence(where Connector evidence lives), andmetadata.
client.verify() and client.version()
client.verify(businessTransactionId):POST /verify, runs a fresh verification and appends a newVerificationto 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. Runnpx 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.