VerdifaxClient

The synchronous Python client. Wraps an httpx.Client with a connection pool, applies client-side validation before every call, and translates HTTP errors into a clean exception hierarchy.

Import

from verdifax import VerdifaxClient

Constructor

VerdifaxClient(
    base_url: str | None = None,
    api_key: str | None = None,
    timeout: float = 30.0,
    transport: httpx.BaseTransport | None = None,
)
ArgumentDefaultNotes
base_url$VERDIFAX_API_URL or http://localhost:9090Trailing slashes are stripped
api_key$VERDIFAX_API_KEYSent as X-Verdifax-Key header
timeout30.0 secondsApplied to every request
transportNoneInject a custom transport for tests (e.g. httpx.MockTransport)

Methods

client.health() -> dict
client.attest(payload, program_id, route_id, registry_record_hash) -> AttestationReceipt
client.verify(manifest_hash, payload, program_id, route_id, registry_record_hash) -> bool
client.execute(payload, program_id, route_id, registry_record_hash) -> ExecutionManifest
client.close() -> None

execute() is a lower-level alternative to attest() that returns just the ExecutionManifest without the receipt envelope.

Context manager

with VerdifaxClient(api_key="vfx_...") as client:
    receipt = client.attest(...)
# connection pool closed automatically

Environment variables

VariableEffect
VERDIFAX_API_URLDefault base URL
VERDIFAX_API_KEYDefault API key

Explicit constructor arguments override env vars.

Connection reuse

For high-throughput servers, instantiate one VerdifaxClient at startup and reuse. Don't construct a new client per request, that re-creates the connection pool and triggers extra TCP handshakes.

Return types and exception handling

client.attest() returns an AttestationReceipt on success. On non-OK outcomes, it raises an exception:

from verdifax import VerdifaxClient, PEPGDenyError, CCVHaltError, MACCHaltError

client = VerdifaxClient()
try:
    receipt = client.attest(payload="...", program_id="...", ...)
    print(receipt.manifest_hash)  # OK run
except PEPGDenyError as e:
    print(f"Policy denied: {e.deny_receipt}")  # pepg_deny
except CCVHaltError as e:
    print(f"Budget halted: {e.ccv_halt_receipt}")  # ccv_halt
except MACCHaltError as e:
    print(f"Cumulative halted: {e.macc_halt_receipt}")  # macc_halt

All three exceptions carry the sealed receipt as an attribute. This lets your application branch on the outcome without parsing the error message.

For lower-level control, client.execute() returns the raw ExecuteResponse object which includes both the outcome and the artifact (manifest + allow_token or just the receipt):

response = client.execute(payload="...", ...)
if response.ok:
    print(response.manifest.manifest_hash)
elif response.outcome_kind == "pepg_deny":
    print(response.deny_receipt)
elif response.outcome_kind == "ccv_halt":
    print(response.ccv_halt_receipt)
elif response.outcome_kind == "macc_halt":
    print(response.macc_halt_receipt)

Continue