> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parmanasystems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK Quickstart

> Get Parmana running end-to-end with the Python SDK: build a transaction, execute it, and read back a signed proof.

<Note>
  This walks through the same flow as the top-level [Quickstart](/quickstart),
  with more explanation at each step and a focus on the SDK's own ergonomics. If
  you just want the fastest path to a working local server, use
  [Quickstart](/quickstart) instead — this page assumes you're already inside a
  local clone of [the Parmana repo](https://github.com/pavancharak/parmana) with
  a server you can point at.
</Note>

## Prerequisites

* **Python 3.8+** (`python --version`)
* **Node.js** (to run the Parmana server locally — `node --version`)
* A local clone of the Parmana repo, with `npm install` already run in it

## Step 1: Start the Runtime locally

From the repo root, override storage to run with no external dependency:

```bash theme={null}
NODE_ENV=test \
  PARMANA_STORAGE=memory \
  PARMANA_POLICY_DIR=/absolute/path/to/policies \
  npm run dev
```

Confirm it's up:

```bash theme={null}
curl http://localhost:3000/health
# {"status":"UP"}
```

With `NODE_ENV=test`, a generic, credential-free test connector (`test-fixture`) registers
automatically — no external credentials needed for this walkthrough. See [Quickstart](/quickstart)
for what each flag does and how to generate a local Gateway keypair if you haven't already.

## Step 2: Install the SDK

```bash theme={null}
pip install parmana
```

Real PyPI package — see [Python SDK](/sdks/python) for the current published version and what's
inside it (generated models, a typed error taxonomy, a real bearer-key `requests.Session()`).

## Step 3: Connect the client

```python theme={null}
from parmana import ParmanaClient

client = ParmanaClient(endpoint="http://localhost:3000", api_key="my-secret-api-key")
```

The committed `.env` in the repo ships one demo caller key for local development:
`callerId: "demo"`, raw key `my-secret-api-key`. `api_key` is optional only against a server
started with `PARMANA_AUTH_DISABLED=true` — every real deployment requires one, and an
omitted/wrong key raises `AuthenticationError`, not a silent failure.

## Step 4: Build a Business Transaction

`create_business_transaction()` derives every id pair the server checks for internal consistency
— see [why that matters](/sdks/python#create_business_transaction-no-more-hand-synced-ids) — so
there's nothing to hand-assemble and get wrong:

```python theme={null}
from parmana import PolicyReference, create_business_transaction

transaction = create_business_transaction(
    principal_id="python-sdk-quickstart",
    purpose="Quickstart demo",
    action="test:fixture-execute",
    target="vendor://payments",
    parameters={"amount": 1000, "currency": "USD"},
    policy=PolicyReference(name="vendor-payment", version="2.0.0", schema_version="1.0.0"),
    signals={
        "vendorVerified": True,
        "invoiceVerified": True,
        "paymentApproved": True,
        "sufficientFunds": True,
        "paymentAmount": 1000,
        "riskScore": 5,
        # vendor-payment@2.0.0 declares boundSignals: { "vendorId": "target" } —
        # this must exactly equal `target` above, checked before policy evaluation
        # ever runs, or the request is rejected as a binding-tamper attempt.
        "vendorId": "vendor://payments",
    },
)

print(transaction.business_transaction_id)
```

`test:fixture-execute` is a generic, credential-free capability built for exactly this kind of
walkthrough — it's unrelated to any real connector, and only registers when the server runs with
`NODE_ENV=test`.

## Step 5: Execute it

One call runs the policy decision and, if approved, the connector — there's no separate
"authorize" step:

```python theme={null}
from parmana import ExecutionRejectedError

try:
    trust_record = client.execution.execute(transaction)
    print(f"Approved: {trust_record.trust_record_hash}")
except ExecutionRejectedError as exc:
    print(f"Denied: {exc}")
```

If the policy denies the transaction, `execute()` raises `ExecutionRejectedError` with the
policy's own reason in the message — it never returns a "REJECTED" status you'd have to check for.

## Step 6: Read the proof

`trust_record` is a real, signed `ExecutionTrustRecord`:

```python theme={null}
print(f"Trust Record ID:     {trust_record.trust_record_id}")
print(f"Trust Record Hash:   {trust_record.trust_record_hash}")
print(f"Signature Algorithm: {trust_record.signature.algorithm}")
print(f"Decision:            {trust_record.executions[0].decision.outcome}")
print(f"Connector evidence:  {trust_record.executions[0].evidence}")
```

This same call, run for real against a local server, is captured verbatim in
[Quickstart, step 7](/quickstart#7-real-output) — expect the same shape (a fresh hash each run,
since the transaction and timestamp differ). See [Execution Trust
Records](/concepts/execution-trust-records) for the complete field-by-field shape, and [Verify
independently](/guides/verify-independently) to check the signature yourself instead of trusting
this output.

## Full script

```python theme={null}
from parmana import (
    ExecutionRejectedError,
    ParmanaClient,
    PolicyReference,
    create_business_transaction,
)

client = ParmanaClient(endpoint="http://localhost:3000", api_key="my-secret-api-key")

transaction = create_business_transaction(
    principal_id="python-sdk-quickstart",
    purpose="Quickstart demo",
    action="test:fixture-execute",
    target="vendor://payments",
    parameters={"amount": 1000, "currency": "USD"},
    policy=PolicyReference(name="vendor-payment", version="2.0.0", schema_version="1.0.0"),
    signals={
        "vendorVerified": True,
        "invoiceVerified": True,
        "paymentApproved": True,
        "sufficientFunds": True,
        "paymentAmount": 1000,
        "riskScore": 5,
        "vendorId": "vendor://payments",
    },
)

try:
    trust_record = client.execution.execute(transaction)
    print(f"Trust Record Hash: {trust_record.trust_record_hash}")
    print(f"Decision: {trust_record.executions[0].decision.outcome}")
except ExecutionRejectedError as exc:
    print(f"Denied: {exc}")
```

The same, already-tested version of this script lives at `python/examples/quickstart/run.py`;
`python/tests/test_quickstart_example.py` runs it against a real, freshly-spawned server on every
test run, so it's verified to work, not just syntax-checked.

## Troubleshooting

**`ConnectionError` / connection refused on `localhost:3000`**
The server isn't running, or isn't listening on the port you're pointing at. Confirm with
`curl http://localhost:3000/health`.

**`AuthenticationError` (401)**
Either no `api_key` was passed against a server that requires one, or the key is wrong. Confirm
the demo key matches what's in the repo's committed `.env`, or that `PARMANA_AUTH_DISABLED=true`
is actually set if you intended to skip auth.

**`ExecutionRejectedError`**
The named policy denied the transaction — the exception message is the policy's own reason. Check
that every signal the policy references is present in `signals`, and that any `boundSignals`
field (like `vendorId` above) exactly matches its bound intent field.

**`ValidationError` (400), often "X must equal Y"**
A hand-built transaction had two of the three cross-checked id pairs out of sync. Use
`create_business_transaction()` instead of constructing the object by hand — it makes this class
of mistake structurally impossible.

## Next

<CardGroup cols={2}>
  <Card title="Python SDK Overview" icon="book-open" href="/guides/python-sdk-overview">
    The concepts behind what you just ran: transactions, decisions, proofs.
  </Card>

  <Card title="Python SDK for AI Agents" icon="robot" href="/guides/python-sdk-ai-agents">
    Wire this into LangChain, CrewAI, FastAPI, and async agent code.
  </Card>

  <Card title="Python SDK Production" icon="server" href="/guides/python-sdk-production">
    Error handling, audit logging, and a deployment checklist.
  </Card>

  <Card title="End-to-end: agent → Parmana → Paytm" icon="route" href="/guides/end-to-end-paytm-flow">
    The same flow against a real connector and real infrastructure, not the
    local test fixture.
  </Card>
</CardGroup>
