What it is
Integrating Parmana means getting a caller, human, AI agent, or automated service, to submit a Business Transaction to a running Parmana instance and receive back a signed Execution Trust Record. There are three real paths to do this: the official TypeScript SDK (typescript/,
published as @parmana/sdk), the official Python SDK (python/, published as parmana), or
plain HTTP against the REST API directly. All three ultimately call the same two routes,
POST /execute and POST /transactions, which run through the identical execution pipeline.
Why it was built this way
A caller hand-building a request body has to keep three id pairs internally consistent (metadata.businessTransactionId must equal the top level businessTransactionId,
authorization.authorityId must equal authority.authorityId, intent.authorizationId must
equal authorization.authorizationId). Getting any of these wrong produces a 400 from
BusinessTransactionValidator. The SDKs exist specifically to make that class of mistake
structurally impossible, not merely documented. typescript/src/builders/createBusinessTransaction.ts
says this directly in its own doc comment: this exact mismatch is “the class of mistake that
produced every ‘X must match Y’ 400 response documented in END-TO-END-FLOW.md.”
How it works
The TypeScript SDK
@parmana/sdk’s entry point is ParmanaClient (typescript/src/client/ParmanaClient.ts). Its
own doc comment is explicit about what it does and does not do: it holds configuration, composes
sub-APIs, and delegates, it does not evaluate policy, authorize execution, execute business
logic, verify trust records, or replay executions, all of that is server side.
Construction requires a Configuration (typescript/src/config/Configuration.ts):
apiKey is optional only against a Runtime started with PARMANA_AUTH_DISABLED=true, which is
local development only, every other deployment rejects an unauthenticated request with a 401
before a Business Transaction is even constructed.
ParmanaClient composes ten sub APIs (HealthApi, ExecutionApi, VerificationApi,
ReplayApi, ReceiptApi, TransactionApi, TrustRecordApi, PolicyApi, RefusalApi,
AuditApi), each a thin wrapper over one or more HTTP calls through the shared Transport. The
public surface includes execute(), createTransaction() (a second, independent entry point
into the identical pipeline via POST /transactions), verify(), getLatestVerification(),
replay(), receipt(), transaction()/transactions(), trustRecord(), validatePolicy(),
refusalRecord()/verifyRefusalRecord(), and verifyAuditEvent().
createBusinessTransaction() (typescript/src/builders/createBusinessTransaction.ts) is the
recommended way to build a request body. It takes the fields a caller actually decides
(principalId, purpose, action, target, parameters, policy, signals, and optional
businessTransactionId, authorityType, correlationId, tenantId, sourceSystem,
submittedBy) and derives every id-consistency field automatically. authorityType defaults to
"SERVICE", described in the source as “the correct value for an autonomous agent” (there is no
"AGENT" value server side, a common first-integration mistake). businessTransactionId
defaults to a fresh crypto.randomUUID() if omitted, and if you provide your own, you are
responsible for its uniqueness, since it doubles as the server’s idempotency key.
The Python SDK
python/parmana/client.py mirrors the same shape: a ParmanaClient, a ClientConfig
(python/parmana/config/client_config.py), and a parallel set of API modules under
python/parmana/api/ (execution_api.py, policy_api.py, transaction_api.py,
trust_record_api.py, verification_api.py, replay_api.py, receipt_api.py,
refusal_api.py, audit_api.py). python/parmana/builders.py is the equivalent of the
TypeScript builder. Requires Python 3.10+ (from pyproject.toml’s requires-python).
The plain HTTP path
Every SDK call ultimately becomes an HTTP request. A minimal integration with no SDK at all needs:- A bearer key:
Authorization: Bearer <key>on every request except/health,/ready,/openapi.yaml,/openapi.json,/api-manifest.json,/documentation,/reference,/refusal/verify,/audit/verify, and the key-discovery routes. - A policy that already exists on the target instance, at the exact
(name, version)you reference, and (ifPOLICY_EXECUTION_VERIFICATION_ENFORCED=trueon that instance) has a realPolicyChangeApprovalRecord(see Chapter 7). - A well formed
BusinessTransactionbody, with every fact your chosen policy’s rules reference present insignals, matching either aboundSignalsentry (derived automatically fromtarget/parametersserver side, but still worth sending consistently) or anunboundSignalReasonsentry (must be supplied directly).
Concrete example
examples/tutorials/ has dedicated SDK walkthroughs (grep examples/tutorials/ for sdk in the
directory name): the TypeScript and Python quickstart, AI-agent, and production-pattern
tutorials each demonstrate a real client construction and a real execute() call against a
locally spun-up server, no live network needed. docs/site/guides/typescript-sdk-quickstart.mdx
and docs/site/guides/python-sdk-quickstart.mdx cover the same ground for the docs site; their
code samples were checked against the actual SDK source above, not copied blind.
How to validate this yourself
typescript/src/client/ParmanaClient.ts,typescript/src/config/Configuration.ts,typescript/src/builders/createBusinessTransaction.ts,typescript/test/for the SDK’s own test suite.python/parmana/client.py,python/parmana/config/client_config.py,python/parmana/builders.py,python/tests/.packages/runtime/src/validators/BusinessTransactionValidator.tsfor the exact cross-consistency checks a hand built transaction must satisfy.END-TO-END-FLOW.md(repo root) for the original incident record of what happens when these checks fail.
Requirements checklist
On the caller side, to make any real call:- A bearer key, provisioned via
npx tsx scripts/generate-api-key.ts --caller-id "<you>" --credential-holder-type <TYPE>.credentialHolderTypemust beUSERif you intend to act as a policy governance checker (Chapter 7);SERVICEis typical for an automated integration. allowedPrincipalIds, if your key is scoped to specific principals, must include whateverprincipalIdyou submit, or the request is rejected before policy runs. Check withGET /callers/me.allowedCapabilities, if scoped, must include theactioncapability you intend to invoke.
.env.example and packages/shared/src/config/Config.ts:
Common first-integration mistakes
- Sending
authorityType: "AGENT", which does not exist server side; use"SERVICE". - Hand-building the three id-consistency fields instead of using
createBusinessTransaction(), and getting one wrong. - Forgetting that
businessTransactionIdis an idempotency key: resubmitting the same id with different content is rejected as a duplicate before execution, not silently overwritten (seedocs/site/guides/end-to-end-paytm-flow.mdx). - Referencing a policy
(name, version)that exists on disk but has never been approved, on an instance wherePOLICY_EXECUTION_VERIFICATION_ENFORCED=true(Chapter 7). On most current deployments this flag is off, so this specific failure mode is currently latent, not active, but worth knowing about before it’s turned on. - Omitting a
signalsentry for a fact the target policy’s rules actually reference; the request is rejected, not silently evaluated with a missing/undefined fact.