Votrix Managed Agents (VMA) Python SDK
votrix is the Python client for the native Votrix Managed Agents API.
AsyncVotrix covers the broader native resource surface; Votrix provides a
synchronous GA wrapper for API-key administration, model-provider discovery,
Vaults, and provider BYOK credentials. Neither client exposes internal
environment-variable names.
The package requires Python 3.10 or newer.
Installation
The SDK has not been published to PyPI yet. Install it from this repository during development:
python -m pip install -e ./sdks/python
After the first release, the published installation command will be:
python -m pip install votrix-managed-agents
From inside sdks/python, install development dependencies with
python -m pip install -e ".[dev]".
This distribution installs only the votrix client package. It does not
install the Votrix Managed Agents server, its app package, or the server's
legacy compatibility namespace, so it can be installed alongside the service
source tree without a package-name collision.
Client setup
Pass the API key and service URL explicitly:
from votrix import AsyncVotrix
client = AsyncVotrix(
api_key="vma_live_...",
base_url="https://vma.example.com",
)
Alternatively, set both environment variables and construct the client with no credentials in application code:
export VMA_API_KEY="vma_live_..."
export VMA_BASE_URL="https://vma.example.com"
from votrix import AsyncVotrix
client = AsyncVotrix()
base_url or VMA_BASE_URL is always required. VOTRIX_VMA_API_KEY and
VOTRIX_VMA_BASE_URL are supported as namespaced aliases. The default
authentication scheme sends x-api-key. For a deployment that accepts bearer
authentication, select it explicitly:
Production keys start with vma_live_; staging, development, local, and test
keys start with vma_test_.
client = AsyncVotrix(
api_key="vma_live_...",
base_url="https://vma.example.com",
auth_scheme="bearer",
)
Use the client as an async context manager so its connection pool is closed deterministically:
import asyncio
from votrix import AsyncVotrix
async def main() -> None:
async with AsyncVotrix() as client:
providers = await client.model_providers.list()
for provider in providers.data:
print(provider.id, provider.display_name)
asyncio.run(main())
If a context manager does not fit the application's lifetime, call
await client.close() during shutdown.
Managed Agents resources
The public-beta client exposes API keys, agents, environments, sessions, files, skills, Memory Stores, Vaults, model Credentials, model providers, and raw usage as typed async resources:
async with AsyncVotrix() as client:
agent = await client.agents.create(
name="support-agent",
model={"id": "deepseek/deepseek-v4-pro", "provider": "openrouter"},
system="Help the end user clearly and concisely.",
)
environment = await client.environments.create(
name="production-runtime",
config={"type": "cloud"},
)
session = await client.sessions.create(
agent=agent.id,
environment_id=environment.id,
vault_ids=["vault_end_user", "vault_organization"],
)
usage = await client.usage.list(session_id=session.id, metric="model_tokens")
Native callers may pass SessionFundingRequest(type="platform_credits") to
sessions.create; the other values are byok and organization_default.
Omission preserves the CMA-compatible request shape and uses the Organization
default. Funding selection is fixed for the lifetime of the Session.
Session creation and event submission automatically send a fresh
Idempotency-Key, which makes the SDK's retry policy safe. Pass
idempotency_key= when the key must survive a caller-level retry or process
restart.
Request and response objects accept the Votrix extensions for their resource while retaining the shared Claude Managed Agents field names where the APIs overlap.
Memory Stores are available through memory_stores, with nested memories and
memory_versions resources. Generic Vault Credentials remain deliberately
absent; provider BYOK uses the typed vaults.model_credentials surface below.
Memory Stores
Create a store, manage path-addressed memories, and inspect or redact immutable versions through the native async client:
store = await client.memory_stores.create(name="Account context")
memory = await client.memory_stores.memories.create(
store.id,
path="/accounts/acme.md",
content="ACME prefers email.",
view="full",
)
memory = await client.memory_stores.memories.update(
memory.id,
memory_store_id=store.id,
content="ACME prefers chat.",
precondition={
"type": "content_sha256",
"content_sha256": memory.content_sha256,
},
view="full",
)
versions = await client.memory_stores.memory_versions.list(
store.id,
memory_id=memory.id,
view="full",
)
first_version = await client.memory_stores.memories.versions.retrieve(
1,
memory_store_id=store.id,
memory_id=memory.id,
)
Use memories.retrieve_by_path(path, memory_store_id=...) (or its by_path
alias) for direct path lookup. A depth-limited memory list may contain
MemoryListItem(type="memory_prefix", ...) directory rollups. Redaction is
available at memory_stores.memory_versions.redact(...); the API rejects
redaction of a live memory's current head version.
API-key lifecycle
Both clients expose scoped Organization-key administration:
created = await client.api_keys.create(
name="production-worker",
scopes=["api", "worker"],
)
save_once(created.secret.get_secret_value())
rotated = await client.api_keys.rotate(created.id, reason="scheduled rollover")
save_once(rotated.secret.get_secret_value())
await client.api_keys.revoke(rotated.id, reason="retired")
The plaintext key is represented as Pydantic SecretStr and is present only
in create() and rotate() results. List, retrieve, and revoke use the safe
ApiKey metadata model, which ignores any unexpected secret field returned by
a misconfigured intermediary. API-key lifecycle methods are also available on
the synchronous Votrix client without await.
Model-provider credentials
Discover provider IDs from the service instead of hard-coding provider secret names:
providers = await client.model_providers.list()
openrouter = next(item for item in providers.data if item.id == "openrouter")
vault = await client.vaults.create(display_name="End-user credentials")
credential = await client.vaults.model_credentials.create(
vault.id,
provider=openrouter.id,
api_key=end_user_api_key,
display_name="Personal OpenRouter key",
)
# Rotation keeps the same credential binding and still hides internal names.
credential = await client.vaults.model_credentials.rotate(
vault.id,
credential.id,
api_key=rotated_end_user_api_key,
)
credentials = await client.vaults.model_credentials.list(vault.id)
credential = await client.vaults.model_credentials.retrieve(
credential.id,
vault_id=vault.id,
)
# Archive permanently purges the stored provider key.
credential = await client.vaults.model_credentials.archive(
credential.id,
vault_id=vault.id,
)
The caller supplies only a stable provider ID and the write-only API key. The
service maps that provider to its internal credential representation; callers
do not send or need to know a secret_name such as OPENROUTER_API_KEY.
create() and rotate() accept a plaintext api_key as a write-only request
field. List, retrieve, rotate, and archive responses never contain that value,
auth, or secret_name. Archive and delete purge the encrypted secret before
changing lifecycle state, so an archived Credential cannot be rotated or used
again. Use delete(credential.id, vault_id=vault.id) when no tombstoned
Credential metadata is needed.
The same BYOK lifecycle is available synchronously:
from votrix import Votrix
with Votrix(
api_key="vma_live_...",
base_url="https://vma.example.com",
) as client:
provider = client.model_providers.retrieve("openrouter")
vault = client.vaults.create(display_name="End-user credentials")
credential = client.vaults.model_credentials.create(
vault.id,
provider=provider.id,
api_key=end_user_api_key,
)
credentials = client.vaults.model_credentials.list(vault.id)
credential = client.vaults.model_credentials.retrieve(
credential.id,
vault_id=vault.id,
)
The synchronous client intentionally exposes provisioning surfaces only: API
keys, model providers, Vaults, and model Credentials. Use AsyncVotrix for
Agents, Sessions, Files, and Skills.
Pagination
List methods return an awaitable paginator. Await it to inspect one page and advance manually:
page = await client.agents.list(limit=25)
for agent in page.data:
print(agent.id)
next_page = await page.get_next_page()
Or iterate over the paginator to traverse all pages lazily:
async for agent in client.agents.list(limit=100):
print(agent.id)
Session event streams
Session streams use server-sent events. Open them with the returned async context manager so the HTTP response is always closed:
async with await client.sessions.events.stream(session.id) as stream:
async for event in stream:
print(event.type, event.seq)
The stream object deliberately requires the async with await ...stream(...)
form before iteration. If the connection ends unexpectedly, it reconnects up
to the client's max_retries, sends the last received SSE ID as
Last-Event-ID, and suppresses replayed event IDs. Set max_reconnects on
stream() to override that limit for one stream. Cancelling the consuming task
or leaving the context closes the active HTTP response.
File downloads
Downloads return a binary response wrapper. Use read() to obtain the bytes:
download = await client.files.download(file_id)
contents = await download.read()
print(download.filename, download.content_type, len(contents))
for chunk in download.iter_bytes(64 * 1024):
process(chunk)
await download.write_to_file("./result.bin")
read() buffers the response for compatibility. For large files, consume a
fresh download incrementally instead:
download = await client.files.download(file_id, stream=True)
async for chunk in download.aiter_bytes(64 * 1024):
process(chunk)
stream=False is the backwards-compatible default, so existing synchronous
iter_bytes() calls still operate on buffered content. With stream=True,
write_to_file() also writes incrementally when the response has not already
been buffered. The SDK never interprets or writes downloaded bytes unless
write_to_file() is called explicitly. Consuming a streaming response closes
it; if one is abandoned before consumption, call await download.aclose() or
use it as an async context manager.
Development
pytest
pyright
python -m build
The package ships inline annotations and a py.typed marker. Stable resource
methods and response fields are typed; intentionally forward-compatible API
metadata and extension fields may remain Any.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file votrix_managed_agents-0.1.0.tar.gz.
File metadata
- Download URL: votrix_managed_agents-0.1.0.tar.gz
- Upload date:
- Size: 43.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfbf579d8ad1a95f51a313c1df0ba71772d8bf8bf0775cb1d68bfe2b4b3dfec8
|
|
| MD5 |
94a9c522a238e676c5274cff782db19a
|
|
| BLAKE2b-256 |
7831ee945d0d018395a89cbb7aee53826f3fd158a433a1816bb3734c0c2055fa
|
Provenance
The following attestation bundles were made for votrix_managed_agents-0.1.0.tar.gz:
Publisher:
python-sdk-publish.yml on votrixai/votrix-managed-agents
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
votrix_managed_agents-0.1.0.tar.gz -
Subject digest:
bfbf579d8ad1a95f51a313c1df0ba71772d8bf8bf0775cb1d68bfe2b4b3dfec8 - Sigstore transparency entry: 2265530771
- Sigstore integration time:
-
Permalink:
votrixai/votrix-managed-agents@7828024ef97113eacb763b6987796fbbb2ff66ca -
Branch / Tag:
refs/tags/sdk-python-v0.1.0 - Owner: https://github.com/votrixai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-sdk-publish.yml@7828024ef97113eacb763b6987796fbbb2ff66ca -
Trigger Event:
push
-
Statement type:
File details
Details for the file votrix_managed_agents-0.1.0-py3-none-any.whl.
File metadata
- Download URL: votrix_managed_agents-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a6745c2eb8eac4ab47a6b5f4883e4c144c76b0976bbf334074784b0226a1be2
|
|
| MD5 |
3d7a967eecdc5cfe84dfc3bf5b75f2b2
|
|
| BLAKE2b-256 |
6bb80f0e3b26ae20cfcfbc4df09fcb32186568427b46809aaf51042aac6897a6
|
Provenance
The following attestation bundles were made for votrix_managed_agents-0.1.0-py3-none-any.whl:
Publisher:
python-sdk-publish.yml on votrixai/votrix-managed-agents
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
votrix_managed_agents-0.1.0-py3-none-any.whl -
Subject digest:
7a6745c2eb8eac4ab47a6b5f4883e4c144c76b0976bbf334074784b0226a1be2 - Sigstore transparency entry: 2265530899
- Sigstore integration time:
-
Permalink:
votrixai/votrix-managed-agents@7828024ef97113eacb763b6987796fbbb2ff66ca -
Branch / Tag:
refs/tags/sdk-python-v0.1.0 - Owner: https://github.com/votrixai
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-sdk-publish.yml@7828024ef97113eacb763b6987796fbbb2ff66ca -
Trigger Event:
push
-
Statement type: