[AVAILABLE], published,
parmana on PyPI. Real, public, installable
with a plain pip install parmana. This page said v1.0.5 and described only
an editable local install until 2026-09-14; both were stale, confirmed against
the live PyPI registry while fixing this page. 79 passing tests, 0
placeholders, black/ruff/mypy --strict all clean across the whole
package. Bearer-key authentication, the error taxonomy below, and
PolicyApi.validate() are all closed and verified against a real running
local server. test_quickstart_example.py proves the documented quickstart
script itself runs. v1.1.2 added create_business_transaction() (see below)
and the offline_verifier.py fix. Two quick follow-up releases, v1.1.3 and
v1.1.4, corrected the published package metadata (repo name, docs domain,
added a Website URL); v1.1.4, published 2026-09-14, is the current version,
confirmed against the live PyPI registry. See Changelog and the
fix history at the bottom of this page.Install
py.typed is shipped, this is a PEP 561 typed distribution, confirmed by installing into
a clean venv and checking parmana.__file__’s directory for the marker.
Bearer-key authentication
ParmanaClient(api_key=...) is sent as Authorization: Bearer <api_key> on every request, set
once on the underlying requests.Session():
api_key is optional, 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, raised as AuthenticationError (see below), not
a silent failure. See Authentication for how keys are minted.
Models are generated, not hand-maintained
Every model inpython/parmana/models/*.py is generated directly from the TypeScript AST
of packages/shared/src/domain/*.ts (and CryptoAlgorithms.ts) by
python/scripts/generate_models.ts, not hand-aligned copies. A drift guard
(npm run check:python-models, wired into CI) regenerates into memory and fails the build
if the committed output would change. Spot-checked field-for-field against the real JSON
schemas (Authority.display_name optional, Authorization.expires_at optional,
BusinessTransactionMetadata correctly optional-except-business_transaction_id,
ExecutionTrustRecord.settlement_confirmations optional): accurate, no drift found.
Enums are real Python str, Enum classes (e.g. SignatureAlgorithm,
VerificationStatus), not bare strings.
Errors, correctly mapped to real conditions
All inherit
ParmanaHttpError → ApiError. Proven against the real HttpTransport (not a
test double) using responses-mocked HTTP built from real, verified response shapes
(python/tests/test_http_transport.py), and against an actual running server in
python/tests/test_live_server_integration.py.
The client also reuses a requests.Session() (connection pooling) and retries idempotent
GETs with backoff on 502/503/504, POSTs are never retried.
Every endpoint the API exposes has a method
client.transactions.create() covers POST /transactions, a second independent entry point
into the identical execution pipeline as execute(), differing only in its 201 status
code. policy.validate takes (policy_id, policy_version), not a policy document, matching
what the route actually reads.
POST /policies/validate never uses the shared {error, code?} envelope at 400/404.
Every status it returns (200, 400, 404) is {valid, errors}, the caller’s answer, not
an SDK-level failure, so policy.validate() returns {"valid": False, "errors": [...]} for
an unknown policy rather than raising NotFoundError. 401 is not exempted: it’s generated
by caller-auth middleware before this route’s own handler runs, using the shared envelope like
every other route. See Error handling.create_business_transaction(), no more hand-synced ids
Added 2026-09-14, ships in v1.1.2, published, available via a plain
pip install parmana. A BusinessTransaction has three id pairs the server’s own
BusinessTransactionValidator cross-checks before policy ever runs:
metadata.business_transaction_id must equal business_transaction_id,
authorization.authority_id must equal authority.authority_id, intent.authorization_id
must equal authorization.authorization_id. Every “X must match Y” 400 documented in
the end-to-end Paytm guide came from hand-building a request
and getting one of those wrong. create_business_transaction() derives all three
automatically:
business_transaction_id defaults to a fresh uuid4() 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. authority_type defaults to AuthorityType.SERVICE (never
"AGENT", which doesn’t exist server-side). See examples/builder/run.py (the same flow as
examples/quickstart/run.py, side by side, for direct comparison) and
tests/test_builders.py (9 unit tests) / tests/test_builder_example.py (proves the
generated ids round-trip correctly through a real running server, not just in isolation).
Test suite
79 tests,pytest, confirmed against a real run: unit tests for every error-mapping case
including the two classification cases above, bearer-key header
attachment tests, create_business_transaction() (9 cases, tests/test_builders.py), and three
real-server integration suites. test_live_server_integration.py spawns the actual
@parmana/api process (npx tsx packages/api/src/server.ts, the same entry point npm run dev
runs) on a real OS-assigned TCP port and drives it with the real ParmanaClient over real HTTP:
a real 401, a real 403, a real 400, a real policy rejection, a real 404, a real 409
duplicate, and the policy.validate() 404 case, each asserted against the exact typed result
and exact message the server returned. test_quickstart_example.py does the same for
run_quickstart(), the quickstart example script itself, additionally asserting its documented
printed output still matches what it actually prints. test_builder_example.py does the same
for run_builder_example() (see above), additionally asserting every builder-derived id pair
round-trips correctly.
ruff/black/mypy --strict are clean across the whole package: python -m ruff check ., python -m black --check ., and python -m mypy all pass with
zero findings.Next
End-to-end: agent → Parmana → Paytm
The full, no-ambiguity runbook this SDK builds toward: real infrastructure,
every error message, every fix.
TypeScript SDK
The other maintained SDK: same bearer-key model, same error taxonomy shape.
Error catalog
Every error the real API returns, independent of any SDK.