Guides

Governance SDK Quickstart (Python)

Install kla-governance, authenticate, and gate one high-risk tool call through the KLA agent action ingress.

4 min read982 words

kla-governance is the KLA governance SDK for Python. It sends agent actions to the KLA Control Plane, receives one of the four policy outcomes (allow, warn, require_approval, block), waits on human approval at the Decision Desk, and returns the server-recorded execution result. KLA's registered connector owns the side effect. The server records the decision, approval, execution, and receipt references.

Support status: version 0.1.0 is published on PyPI. Version 0.2.0 adds the api_key option and PolicyClient. Framework coverage is tracked by the agent compatibility matrix in agent-compatibility/v1/; the SDK's adapter helpers are structural and carry no framework imports, and no framework is claimed as supported here.

Install

pip install kla-governance

Requires Python 3.10 or later. The only runtime dependency is httpx.

Authenticate

The SDK needs three things: a base URL, a credential, and the agent binding (agent id, pinned release version, and an active mandate id). Use a tenant API key with the actions:propose scope: a tenant administrator creates it in Settings > API keys or with kla api secrets createApiKey, and the SDK sends it as x-kla-api-key. A client-credentials access token passed as credential also works; a client takes one of the two. The key or the verified token selects the tenant; the SDK never sends a tenant field in the request body.

import os

from kla_governance import AgentIdentity, KLAClient

client = KLAClient(
    base_url="https://api.dev.kla.digital",
    api_key=os.environ["KLA_API_KEY"],
    agent=AgentIdentity(agent_id=os.environ["KLA_AGENT_ID"], release_version="1.4.2"),
    mandate_id=os.environ["KLA_MANDATE_ID"],
)

binding = client.resolve_binding()
print(f"Mandate revision {binding.mandate.version} is active")

AsyncKLAClient mirrors the same API with async methods.

Register the tool and its connector in the Tool Catalog before using the SDK. The active mandate must cover the agent, tool, operation, and destination shown in the action. resolve_binding is a read-only startup check of the current approved Release, mandate revision, status, validity window, and allowed actions.

Create the mandate

A mandate states which tools, operations, destinations, and purposes the agent may act on, under which controls, and for how long. It binds to the agent's active production Release, so approve and deploy a Release first (Govern an Agent End-to-End). Write the mandate file:

accountableOwner: Head of Payments Operations
institutionalPrincipal: Example Bank
allowedActions:
  - tool: payments
    operations: [charge]
    destinations: ['stripe:acct_main']
purposes: [Invoice payment]
environments: [production]
controlRefs:
  - controlId: payment-controls
    version: 1.0.0
validFrom: '2026-10-10T00:00:00.000Z'
validUntil: '2027-01-08T00:00:00.000Z'

controlRefs names the published policy versions that govern the actions; a mandate needs at least one control reference or threshold constraint. Create, submit, approve, and activate it:

kla agent mandate create "$KLA_AGENT_ID" --file mandate.yaml
kla agent mandate submit <mandate ID>
kla agent mandate get <mandate ID>
kla agent mandate approve <mandate ID> --version <version> --reason 'Scope matches the payment procedure.'
kla agent mandate activate <mandate ID>

create fills in the active Release and rollout. A second person with the kla:approver role reads the mandate with get and runs approve with the version they read; KLA refuses an approval by the mandate's author. kla agent mandate list --agent "$KLA_AGENT_ID" shows each mandate and its status.

Gate one action

from kla_governance import ActionRequest, DecisionDenied

def process_payment(account_id: str, amount: float):
    try:
        return client.execute_action(
            ActionRequest(
                tool="payments",
                operation="charge",
                destination="stripe:acct_main",
                parameters={"account_id": account_id, "amount": amount},
            ),
            proposal_id=stable_id_for(account_id, amount),
            timeout=15 * 60,
        )
    except DecisionDenied as denial:
        return {"declined": True, "reason_codes": denial.reason_codes}

execute_action always settles to a server execution. allow and warn execute immediately. A require_approval action waits for a Decision Desk verdict and redeems that same proposal identity. The method returns execution.result. block, a denied approval, and an expired approval raise DecisionDenied. Branch on reason_codes; the remediation text is for humans.

Approval handling

  • Run gated calls on a worker or background task. A reviewer can take minutes or days.
  • Pass timeout to bound the wait. ApprovalTimeout means the Decision Request remains pending; call execute_action again with the same proposal_id to continue.
  • Cancelling an async task stops its local wait. The server proposal stays open. Call cancel_action(proposal_id, reason=...) to close a received or approval-pending proposal.
  • ExecutionMissing is the final defensive guard when a settled decision lacks a server execution record. The SDK fails closed and does not invoke a local destination.
  • ExecutionEvidencePending means the connector ran while evidence sealing is incomplete. The SDK withholds every result in this state. The error retains the decision and receipt references. Reconcile the same proposal identity.
  • KLATransportError with code="response_schema_invalid" means the response failed the exact schema, proposal/action binding, lifecycle matrix, result hash, or receipt checks. No output is released.

Retry rules

  • Reuse the same proposal_id for retries of the same logical action. A committed proposal replays its recorded outcome.
  • 503 responses and network errors are retried automatically with backoff and jitter.
  • 4xx responses are terminal for that proposal and surface as KLATransportError with retryable=False. Generate a new proposal_id only for a genuinely new action.
  • The typed Revoked, EvidenceUnavailable, ExecutorUnavailable, and Cancelled errors identify common terminal and service-failure branches.

Local tests

Use the exported LocalSandboxClient with static scenarios. It has no network or destination callback. Its call records redact parameter values. Results carry production=False and environment="local-sandbox", so test results remain distinguishable from live KLA execution.

Troubleshooting

  • 404 on /v1/agent-actions: the ingress is behind KLA_AGENT_ACTION_INGRESS_ENABLED and is disabled in that environment.
  • 401: the API key is revoked, expired, or mistyped, or the Bearer token is missing, expired, or issued for another audience.
  • 403 api_key_scope_denied: the API key lacks the actions:propose scope.
  • 409 release_mismatch: release_version differs from the active approved rollout for the tenant and environment.
  • 403 mandate_invalid, mandate_revoked, mandate_expired: the mandate does not cover the tool, operation, and destination, or is no longer active.
  • 409 args_mismatch on resume: the action arguments changed between approval and redemption. Propose a new action.
  • 410 approval_expired on resume: the approval passed its dueAt. Propose a new action.
  • proposal_cancelled: the proposal closed before execution. Start a new proposal for a new action.
Governance SDK Quickstart (Python) | Developer Docs | KLA Control Plane