[AVAILABLE],
typescript/src/, v1.0.0. The original three gaps (no bearer-key auth, an
unused error taxonomy, an empty test suite) were closed in an earlier pass. A follow-up full SDK
audit found and fixed three more real issues: inaccurate model types (fields marked required that
the real API doesn’t require, and a missing settlementConfirmations/signature field), two
missing capabilities (POST /verify, GET /version), and one capability missing from both
maintained SDKs (POST /transactions). All re-verified against a real running local server. See
the fix history at the bottom of this page.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) — found live during this pass and
added to the Error catalog and the OpenAPI spec alongside this
fix, both re-verified against source and a live server.
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, corrected against the real schemas
typescript/src/models/*.ts is hand-maintained, unlike the Python SDK’s generated models — a
full audit spot-checked every field against the real JSON schemas
(schemas/common/*.schema.json) and found real inaccuracies, all fixed this pass:
Authority.displayName,Verification.message— were marked required; the real API makes them optional.Authorization.expiresAt— missing entirely; now present (optional).BusinessTransactionMetadata— every field exceptbusinessTransactionIdwas marked required; the real API requires onlybusinessTransactionId. The most significant of these: it forced callers to supply fields (tenantId,correlationId,sourceSystem,submittedBy,submittedAt) the real API doesn’t need.ExecutionTrustRecord— was missing the requiredsignaturefield and the optionalsettlementConfirmationsfield (and theSettlementConfirmation/Signaturemodels themselves) entirely.Override.authorityId— wrong field name, should beapprovedBy(the real schema has noauthorityIdonOverrideat all); also missing the optionaljustificationfield. Currently unreachable in practice (no route in this repository creates anOverride), but wrong is wrong.Decision.outcome,Execution.status,Execution.mode,BusinessTransaction.status— were barestring; now literal unions matching the real enums exactly.Execution— was missingcompletedAt,evidence, andmetadataentirely: real, commonly-used fields (evidence in particular is where Connector evidence lives) with no way to read them through this SDK’s types before this pass.
Two capabilities added this pass, previously unreachable from this SDK
client.verify(businessTransactionId)—POST /verify, runs a fresh verification and appends a newVerificationto the record’s history. Before this pass, the SDK could only read a cached verification viagetLatestVerification()(GET /verification/:id); there was no way to trigger a fresh one at all.client.version()—GET /version. Had no method anywhere in the SDK.
POST /transactions, missing from both maintained SDKs, now added
client.createTransaction(transaction) — a second, independent entry point into the identical
execution pipeline as execute() (POST /execute), differing only in its 201 status code.
Confirmed stable before adding: the route has existed since near the start of this repository’s
history (18 commits into a 141-commit history), was touched by the most recent security-fix
commit in lockstep with /execute, and has dedicated test coverage
(packages/api/tests/unit/transactions-api.test.ts) — not a new or evolving surface.
Install
Test suite
Previously 9 files, 0 bytes each, “9 passed” trivially. Now 129 tests across 15 files,npm run test (vitest run), confirmed against a real run this pass: unit tests for every error-mapping
case (test/HttpTransport.test.ts, test/Errors.test.ts), unit tests for the three
capabilities added in the model-audit pass (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, see docs/VERIFICATION-GAPS.md G-27) — on a
real OS-assigned TCP port and drive it with 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).
What’s still true from before
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, for the same reason they were
before this pass: inventing a throw condition for them would be describing behavior the real API
doesn’t have.
Next
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.