Kora Python SDK
Python SDK for the Kora authorization engine. Handles Ed25519 signing, nonce generation, canonical JSON serialization, idempotent retry, and offline seal verification.
Installation
pip install kora-sdk
Or install from source:
pip install -e sdk/python
Requirements: Python 3.9+, PyNaCl >= 1.5.0, requests >= 2.28.0
Quick Start
from kora import Kora
# Initialize with the secret key returned from agent creation
kora = Kora("kora_agent_sk_...")
# Authorize a spend
auth = kora.authorize(
mandate="mandate_abc123",
amount=50_00, # EUR 50.00
currency="EUR",
vendor="aws",
category="compute", # required if mandate has category_allowlist
)
if auth.approved:
print(f"Approved: {auth.decision_id}")
print(f"Daily remaining: {auth.limits_after_approval['daily_remaining_cents']}")
else:
print(f"Denied: {auth.reason_code}")
print(f"Hint: {auth.denial.hint}")
Usage
Authorize a Spend
from kora import Kora
kora = Kora(
"kora_agent_sk_...",
base_url="http://localhost:8000", # default
ttl=300, # default TTL in seconds
max_retries=2, # automatic idempotent retry on network error
)
result = kora.authorize(
mandate="mandate_abc123",
amount=50_00,
currency="EUR",
vendor="aws",
category="compute",
)
Result Properties
result.approved # bool — True if APPROVED
result.decision # "APPROVED" or "DENIED"
result.decision_id # UUID of the authorization decision
result.reason_code # "OK", "DAILY_LIMIT_EXCEEDED", etc.
result.executable # bool — True if payment can be executed
result.is_valid # bool — True if TTL has not expired
result.is_enforced # bool — True if enforcement_mode == "enforce"
result.enforcement_mode # "enforce" or "log_only"
# On denial:
result.denial.hint # Human-readable suggestion
result.denial.actionable # Machine-readable corrective values
result.denial.failed_check # Which pipeline step failed
# On approval:
result.limits_after_approval # Remaining daily/monthly budget
# Evaluation trace:
result.evaluation_trace.steps # List of pipeline step results
result.evaluation_trace.total_duration_ms # Total evaluation time
# Notary seal:
result.notary_seal.signature # Ed25519 signature (base64)
result.notary_seal.public_key_id
result.notary_seal.algorithm # "Ed25519"
# Trace URL (for debugging denials):
result.trace_url # e.g. http://localhost:8000/v1/authorizations/<id>/trace
Handle Denials
result = kora.authorize(
mandate="mandate_abc123",
amount=999_99,
currency="EUR",
vendor="aws",
)
if not result.approved:
print(f"Denied: {result.reason_code}")
print(f"Hint: {result.denial.hint}")
# Machine-readable corrective values
if result.reason_code == "DAILY_LIMIT_EXCEEDED":
available = result.denial.actionable["available_cents"]
print(f"Available budget: {available} cents")
if result.reason_code == "VENDOR_NOT_ALLOWED":
allowed = result.denial.actionable["allowed_vendors"]
print(f"Allowed vendors: {allowed}")
# Full trace URL for debugging
print(f"Trace: {result.trace_url}")
Verify Notary Seal (Offline)
from base64 import b64decode
# Kora's public key (from your deployment)
kora_public_key = b64decode("...")
is_valid = kora.verify_seal(result, kora_public_key)
print(f"Seal valid: {is_valid}")
Simulation Mode
Test denial scenarios without affecting state. Requires an admin key with simulation_access=true.
result = kora.authorize(
mandate="mandate_abc123",
amount=100,
currency="EUR",
vendor="aws",
simulate="DAILY_LIMIT_EXCEEDED",
admin_key="kora_admin_...",
)
assert result.simulated is True
assert result.decision == "DENIED"
assert result.reason_code == "DAILY_LIMIT_EXCEEDED"
assert result.notary_seal is None # no seal in simulation
OpenAI Function Tool Schema
Generate an OpenAI-compatible function tool definition for use with LLM agents:
tool = kora.as_tool("mandate_abc123")
# Returns:
# {
# "type": "function",
# "function": {
# "name": "kora_authorize_spend",
# "description": "Authorize a spend against a Kora mandate...",
# "parameters": {
# "type": "object",
# "properties": {
# "amount_cents": {"type": "integer", "description": "..."},
# "currency": {"type": "string", "description": "..."},
# "vendor_id": {"type": "string", "description": "..."},
# },
# "required": ["amount_cents", "currency", "vendor_id"]
# }
# }
# }
# With category enum constraint:
tool = kora.as_tool("mandate_abc123", category_enum=["compute", "api_services"])
Use with OpenAI:
import openai
client = openai.OpenAI()
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Buy $50 of AWS compute"}],
tools=[kora.as_tool("mandate_abc123")],
)
Agent Self-Correction Pattern
from kora import Kora
kora = Kora("kora_agent_sk_...")
# First attempt — too large
auth = kora.authorize(mandate="mandate_abc123", amount=999_99, currency="EUR", vendor="aws")
if not auth.approved and auth.reason_code == "DAILY_LIMIT_EXCEEDED":
# Read the actionable hint
available = auth.denial.actionable["available_cents"]
print(f"Budget available: {available} cents, retrying...")
# Retry with corrected amount
auth = kora.authorize(mandate="mandate_abc123", amount=available, currency="EUR", vendor="aws")
print(f"Second attempt: {auth.decision}") # APPROVED
API Reference
Kora(key_string, base_url=None, ttl=300, max_retries=2)
| Parameter | Type | Default | Description |
|---|---|---|---|
key_string |
str | required | Agent secret key (kora_agent_sk_...) |
base_url |
str | http://localhost:8000 |
Kora API base URL |
ttl |
int | 300 | Default TTL for decisions (seconds) |
max_retries |
int | 2 | Automatic retries on network error |
kora.authorize(**kwargs) -> AuthorizationResult
| Parameter | Type | Required | Description |
|---|---|---|---|
mandate |
str | yes | Mandate ID |
amount |
int | yes | Amount in cents |
currency |
str | yes | 3-letter currency code |
vendor |
str | yes | Vendor identifier |
category |
str | no | Spending category |
simulate |
str | no | Force denial reason code (simulation mode) |
admin_key |
str | no | Admin key for simulation access |
kora.verify_seal(result, public_key) -> bool
Verify the Ed25519 notary seal offline.
kora.as_tool(mandate, category_enum=None) -> dict
Generate OpenAI function tool schema.
Metadata
Release files for kora-sdk 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kora_sdk-1.3.0.tar.gz | 28.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kora_sdk-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 48.8 kB
Release files / kora_sdk-1.3.0.tar.gz
| Download URL | kora_sdk-1.3.0.tar.gz |
|---|---|
| Size | 28.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f280c2a474c97e5b7fc17294eeba6c6791d865655691253d7df60108dd4b4f3
|
|
BLAKE2b-256 checksum How to use checksums |
b87a56da736dd7b6f657d4d9183a0830dc53d66661bd1618488189384ea6dc0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.1
|
Release files / kora_sdk-1.3.0-py3-none-any.whl
| Download URL | kora_sdk-1.3.0-py3-none-any.whl |
|---|---|
| Size | 20.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4eebaa77cb2c7f11c76ad45a89b598daa89222fe3f35f3a139290e13082010d4
|
|
BLAKE2b-256 checksum How to use checksums |
d3eadb8149d26c15337502a6e1539bf7fbc2cf8bf1ab7aacd2ecdae073b70519
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.1
|