Skip to main content

Zanii Mandate Python SDK

Python 3.12+; standard-library runtime only.

python -m pip install ./packages/sdk-python
import os
from zanii_mandate import MandateClient

client = MandateClient(
    verify_base_url=os.environ["ZANII_VERIFY_URL"],
    api_key=os.environ["ZANII_API_KEY"],
    timeout=5.0,
    cache_capacity=128,
    cache_ttl=5.0,
)
decision = client.verify({
    "mandateId": os.environ["ZANII_MANDATE_ID"],
    "request": {
        "agentDid": os.environ["ZANII_AGENT_DID"],
        "action": "supplier.read",
        "resource": "supplier:acme",
    },
}, operation_id="checkout-attempt-2026-001")
assert decision["verdict"] == "allow"

Use verify_batch(items, operation_id=...) for 1–100 items. Reuse the same caller-owned operation ID only for an identical retry. It maps to Idempotency-Key; replay does not consume quota twice.

Verdicts are returned values. MandateApiError, MandateTransportError, MandateTimeoutError and MandateResponseError distinguish HTTP, network, deadline and malformed/oversized-response failures. Public keys() and status() metadata use independent bounded single-flight TTL/LRU caches; verdicts are never cached. An optional tracer exposes the standard start_as_current_span(name, attributes=...) OpenTelemetry shape.

Typed verdicts and reason codes (0.2.0)

Verdicts stay plain dicts, so decision["verdict"], decision["checks"] and fields a newer server adds keep working. zanii_mandate.types adds TypedDict hints for type checkers: Verdict (with conditions: list[ConditionResult], each index, type, ok, code, reason, evidence), Reason, VerifyInput, VerifyRequest (agentDid, action, resource, amount: {"value", "currency"}, dataClass, jurisdiction, approvalId(s), dataSubject, counterpartyDid, dataRegion, destination, transferSafeguard) and Amount. verify accepts a VerifyInput or any dict as before. MandateApiError carries status, code, detail and the server details.

zanii_mandate.codes defines one constant per verdict reason and API error code (codes.ACTION_DENIED == "ACTION_DENIED"), plus the REASON_CODES set, equal to the shared list tests/fixtures/reason-codes.json that the Go, Java and Node.js SDKs also follow. Codes are open strings: never treat an unlisted code as success.

Unknown outcomes

is_unknown_outcome(error) is true when the server answered DATABASE_COMMIT_UNKNOWN (HTTP 503: the connection was lost during COMMIT, so the write may or may not have landed) or when a timeout or transport failure happened after the request may have reached the server. A refused connection or DNS failure is false. Every MandateTimeoutError counts as unknown, because urllib cannot tell a connect timeout from a slow answer. When it is true, mark the operation unknown (mark_operation_unknown) or read the state back with the same operation ID, and never redo the business action. Re-sending the same verify with the same operation ID only replays the stored result.

import json

from zanii_mandate import MandateError, is_unknown_outcome

try:
    client.commit_operation(operation_id, outcome)
except MandateError as error:
    if is_unknown_outcome(error):
        client.mark_operation_unknown(operation_id, {"payload": json.dumps({"reason": "commit outcome unknown"})})  # text, not a dict
        raise  # reconcile later; do not repeat the action
    raise

Example:

PYTHONPATH=packages/sdk-python/src python packages/sdk-python/examples/verify.py checkout-attempt-2026-001

Conformance and package commands:

python -m unittest discover -s packages/sdk-python/tests -v
python -m pip wheel --no-deps --no-build-isolation packages/sdk-python

Operation-bound FastAPI and Django middleware

Install the framework extra (zanii-mandate-sdk[fastapi] or zanii-mandate-sdk[django]). Import mandate_operation from zanii_mandate.fastapi_middleware or zanii_mandate.django_middleware and decorate a route/view with client and a server-owned operation mapping. The asynchronous FastAPI decorator runs the synchronous SDK transport in worker threads; the Django decorator supports synchronous views.

The mapping uses the operation API field names: mandateId, agentDid, action, audience, enforcementPoint, policyRevision, authorityEpoch, argumentSchema, and either static resource or resources plus resourceArgument. Optional fields are mandateVersionId, amount/amountArgument + currency, and count. String, decimal-string, boolean, and non-negative safe-integer arguments are allowlisted. Python decimal strings are required instead of non-integer JSON floats so canonical argument bytes remain interoperable with the Node JCS profile.

Request JSON may contain only args, stable expiresAt, approvalId, and approvalIds; caller claims for action/resource/identity/policy are rejected. Supply the stable operation ID in Idempotency-Key or X-Zanii-Operation-Id. Both adapters reserve and dispatch through MandateClient before calling the endpoint/view. Only fresh allow + dispatched executes. Deny/review return reason-bearing 403 responses; OPERATION_MISMATCH stays 409; timeouts return 504; transport/verification errors never execute.

A 2xx result is committed with the reserved amount/count and an SDK-observed outcome payload. Non-2xx results and endpoint/view exceptions are marked unknown. This is usage accounting, not a signed external execution receipt. An operation replay returns the stored operation result with Idempotency-Replayed: true, without executing again or consuming usage; it does not reconstruct the arbitrary original endpoint/view response. Commit failure cannot undo an external effect.

Executed locally: 17/17 Python tests passed at T7 acceptance (27 October 2026): the original 7 SDK tests over all 12 shared real-PostgreSQL/HTTP vectors plus 10 real FastAPI/Django middleware tests. Both frameworks ran actual loopback HTTP servers against the real verify-api and isolated PostgreSQL 17.11. Tests cover allow/exact accounting, replay without re-execution, argument tampering, deny/review reason propagation, verification error, timeout, and trusted mapping. Django 5.2.8 was installed into the ignored .local/t7-python-deps test directory; no global package was installed. A wheel built successfully. The T5-3 offline evaluator is provided by the Node package; no Python offline cryptographic verifier, package-index publication, production deployment, or exactly-once remote-effect guarantee is claimed.

Changelog

0.2.1 (30 October 2026)

  • Documentation fix only, no code change: the unknown-outcome example now sends the operation payload as text (json.dumps({"reason": ...})). The server refuses an object payload with INVALID_OUTCOME, so the 0.2.0 example would have failed when used.

0.2.0 (30 October 2026)

  • zanii_mandate.codes constants, REASON_CODES and UNKNOWN_OUTCOME_CODES, equal to the shared list tests/fixtures/reason-codes.json (130 codes) used by all four SDKs.
  • is_unknown_outcome(error), with the Go and Java semantics.
  • TypedDict hints (Verdict, ConditionResult, Reason, VerifyInput, VerifyRequest, Amount); verdicts remain plain dicts. No change for 0.1.0 callers.

0.1.0

  • First release: verify and batch, operation lifecycle, key and status caches, telemetry, FastAPI and Django middleware.

Metadata

Release files for zanii-mandate-sdk 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zanii-mandate-sdk 0.2.1
File Size Uploaded Provenance Actions
zanii_mandate_sdk-0.2.1.tar.gz 27.2 kB Without provenance Details for zanii_mandate_sdk-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for zanii-mandate-sdk 0.2.1
File Interpreter ABI Platform Actions
zanii_mandate_sdk-0.2.1-py3-none-any.whl Python 3 none any Details for zanii_mandate_sdk-0.2.1-py3-none-any.whl

Total release size: 48.9 kB

Release files / zanii_mandate_sdk-0.2.1.tar.gz

Download URL zanii_mandate_sdk-0.2.1.tar.gz
Size 27.2 kB
Tags Source
SHA-256 checksum
How to use checksums
63f290f576dee0cfc8d1125b930542217614bec6d959de478ab66573defcd6bd
BLAKE2b-256 checksum
How to use checksums
7df59b86123f8813b1c174173432b0c48d6ee04c13de69f684f8d20cd032f534
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / zanii_mandate_sdk-0.2.1-py3-none-any.whl

Download URL zanii_mandate_sdk-0.2.1-py3-none-any.whl
Size 21.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4242b9f05a814b93dfb1593ff82afb8e4581d96b4f187dc3e19cbe382db17347
BLAKE2b-256 checksum
How to use checksums
3e6c3a5e58eafd2014a368564b2b67592bdf3c2e6c27ccef109cb0b7c40dafbd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Security for zanii-mandate-sdk 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page