All SDKs
Py

Python SDK

vadyl
Available
The Python SDK is a peer projection of your product model and canonical contract backbone. asyncio-first. Compatible with FastAPI, Django, and Jupyter. Python 3.10+.

Install

pip
pip install vadyl
poetry
poetry add vadyl
uv
uv add vadyl
pdm
pdm add vadyl
Read the documentation
1. Initialize

Create the client

Configure the client once. Same object reaches every product surface — entities, workflows, agents, realtime.

client.pyPython
from vadyl import Client
import os

vadyl = Client(
    api_url="https://api.vadyl.app/v1",
    token=os.environ["VADYL_TOKEN"],
    tenant="acme",
    project="billing",
    # optional — pin to a feature branch
    branch=os.environ.get("VADYL_BRANCH"),
)
2. Read

List, filter, paginate

Typed filter expressions, cursor pagination, eager relation loading.

list_orders.pyPython
# Typed filter, sort, pagination, and relation expansion
recent = vadyl.orders.list(
    filter={
        "status":    {"in": ["paid", "fulfilled"]},
        "total":     {"gt": 100},
        "createdAt": {"gte": "now-30d"},
    },
    sort=[{"field": "createdAt", "direction": "desc"}],
    page_size=50,
    include=["customer", "items.product"],
)

# recent.data: list[Order]
# recent.page.next: cursor for next page
print(len(recent.data), recent.page.next)
3. Write

Create, update, delete with idempotency

Every mutation supports idempotency keys. Retries are safe by default.

create_customer.pyPython
# Idempotent create — safe to retry
customer = vadyl.customers.create(
    {"email": "ada@example.com", "name": "Ada Lovelace"},
    idempotency_key=f"signup:{session.id}",
)

# Update with optimistic concurrency
updated = vadyl.customers.update(
    customer.id,
    {"name": "Ada L."},
    if_match=customer.concurrency_token,
)

# Delete (soft-delete on entities with that lifecycle)
vadyl.customers.delete(customer.id)
4. Realtime

Subscribe to entity changes

Field names only — never values. Re-read for values through CRUD with proper access enforcement.

subscribe.pyPython
# Sync streaming subscription with the same filter AST as queries
with vadyl.orders.subscribe(
    filter={"status": {"eq": "paid"}},
    kinds=["created", "updated"],
) as sub:
    for evt in sub.events():
        # evt.kind, evt.entity_id, evt.changed_fields, evt.occurred_at
        # Field NAMES only — re-read for values
        order = vadyl.orders.read(evt.entity_id)
        print("paid:", order)

# Or async
async with vadyl.aio.orders.subscribe(...) as sub:
    async for evt in sub: ...
5. Workflows

Start and signal durable workflows

Long-running product behavior that survives process restart. Same client interface; canonical durability underneath.

workflow_client.pyPython
# Start a durable workflow
run = vadyl.workflows.fulfill_order.start(
    order_id="ord_abc",
)

# Send a signal (e.g. from a webhook)
vadyl.workflows.fulfill_order.signal(
    run.id, "shipped",
    {"trackingNumber": "1Z..."},
)

# Query run state
state = vadyl.workflows.fulfill_order.query(run.id)
6. Agents

Run agents against the product model

Typed plan IR, capability-aware model routing, token accounting — all behind one method.

run_agent.pyPython
# Run an agent — typed plan IR, capability-aware model routing
run = vadyl.agents.SupportAgent.run(
    prompt="Refund order #12345 due to defect",
    user_id=session.user_id,
    budget={"max_tokens": 50_000, "max_tool_calls": 30},
)

# Stream the run
for step in run.stream():
    print(step.kind, step.summary)

# Recall memory for an agent
facts = vadyl.agents.SupportAgent.memory.recall(
    kind="preference",
    subject=session.user_id,
)
7. Errors

Typed error contract

Stable error codes, stable reason codes, correlation IDs, and explainability links.

errors.pyPython
from vadyl import (
    VadylError,
    AccessDeniedError,
    ValidationError,
    ConflictError,
    QuotaExceededError,
)

try:
    vadyl.orders.update(id, {"status": "refunded"})
except AccessDeniedError as err:
    print(err.reason_code, err.correlation_id)
except ConflictError:
    # optimistic-concurrency mismatch — re-read and retry
    ...
except VadylError as err:
    print(err.code, str(err))
8. Authoring runtime

Define handlers, workflows, agents

The same package you use as a client also powers your authored runtime — defineCoreHandler, defineWorkflow, defineAgent.

src/handlers/orders/charge.pyPython
from vadyl import handler

@handler.core
async def charge_order(ctx, order_id: str):
    order = await ctx.entities.Order.read(order_id)

    charge = await ctx.connections.stripe.create_charge(
        amount=order.total,
        currency=order.currency,
        idempotency_key=f"charge:{order.id}",
    )

    await ctx.entities.Order.update(order.id, {
        "status": "paid",
        "chargeId": charge.id,
    })

    await ctx.events.emit("order.paid", {"orderId": order.id})
    return {"ok": True, "charge_id": charge.id}

What you get

Fully typed entities

Pydantic v2 models for every entity, field, relation, and enum. Static-type-checkable end-to-end.

Manifest verification

The SDK refuses to talk to a server whose generated-format version it does not support. Fail closed.

Realtime built-in

WebSocket and SSE subscriptions with the same predicate AST as queries. Reconnect-safe.

Sync & async

Both blocking (Client) and async (AsyncClient) APIs from the same package. Use what fits your stack.

Framework-friendly

Plays nicely with FastAPI, Django, Flask, Starlette, Jupyter notebooks, scripts. No magic globals.

Branch-aware

Pin to a feature branch via VADYL_BRANCH or the branch parameter. Sandbox testing without leaving prod.

Observability hooks

Hook into the canonical envelope. Emit operational trails from the client when needed.

Agent primitives

vadyl.agents exposes the Agent plane: definitions, runs, memory, MCP exposure.

Connections

Typed governed-connection clients per registered governed connection.

Build with the Python SDK in minutes.

Install the package and work with your product model through typed entities, workflows, agents, and events.