Client
HTTP client lifecycle and connection reuse behavior.
#Client
Client is the low-level HTTP and thread API used by Agent and Swarm. Most applications should start with those higher-level objects.
#Construction
from maivn import Client
client = Client(
api_key="...",
client_timezone="America/Chicago",
timeout_seconds=30,
)Client accepts an API key, a base URL, ClientConfig, an optional HTTPX transport, timeout controls, an initial thread ID, and an optional private-data store.
Omitting private_data_store selects automatic encrypted local storage for caller-declared private values. The required private-data-vault dependency opens lazily. Supported desktops use the OS credential store; servers supply MAIVN_VAULT_DIRECTORY and MAIVN_VAULT_KEY, or pass local_vault=LocalVaultConfig(directory=..., key_provider=...). Import LocalVaultConfig from maivn.
Explicit private_data_store=None disables local persistence. A custom PrivateDataStore replaces the automatic store; InMemoryPrivateDataStore retains values only for the lifetime of that store instance. Do not combine a custom store with local_vault configuration.
Use client.forget_private_data(thread_id) or await client.aforget_private_data(thread_id) to delete that thread's saved caller values locally. These methods do not delete server records or uploaded files. See Values that outlive one call for setup and recovery requirements.
restore_private_values defaults to True, which restores private values in the output you get back. Pass restore_private_values=False to keep the final message and structured result masked: they carry placeholders instead of the real values. Arguments for your local tools are still resolved, so those tools still receive real values. See Keep private values masked in output.
env_file= and api_key_file= accept a string or Path naming a selected dotenv or single-line key file. Direct credentials win over the terminal environment, which wins over file values. MAIVN_API_KEY_FILE selects a key file when no higher-priority key value exists. Files are never searched for automatically, and an explicit config= remains authoritative. See credential sources and precedence.
When an explicit value is omitted the environment supplies it: MAIVN_API_KEY for the key, and MAIVN_BASE_URL for base_url (default https://api.maivn.io). There is one public address; the SDK does not accept or read a separate address for any other request grouping. See Choosing which mAIvn endpoint to call.
#One-shot execution
invoke() returns an InvokeResponse, and stream() yields StreamEvent values. Async equivalents are ainvoke() and astream().
from maivn import Client
client = Client(api_key="...")
response = client.invoke("Summarize this incident")
print(response.final_message or response.response)
for event in client.stream("Explain the remediation"):
print(event.name, event.payload)Execution methods accept options, tool metadata, private data, force_final_tool, and swarm-member descriptors. They do not accept the removed status_messages, force_model, or per-call memory/swarm configuration arguments.
#Resources, skills, and memory
The client exposes resources, skills, and memory facades. Resource operations include upload, binding, metadata updates, soft deletion, and restoration. client.memory.skills and client.memory.insights support CRUD; insights also support promotion. Read post-run persistence receipts through client.memory.enrichments.list(session_id).
See SDK Resource Configuration for method names, request models, async counterparts, and the distinction between constructor declarations and stored-asset operations.
#Generated message attachments
Successful generated files are returned in response.artifacts and on the assistant message. Treat each reference as an immutable revision. Its logical_output_id connects later revisions. A filename does not identify an editable file. Replies can carry up to 32 references, matching the run limit.
Use the returned reference to download and verify the exact bytes:
from pathlib import Path
from mimetypes import guess_extension
# response is the completed result from your file-producing Agent or Client.
destination = Path("downloads")
destination.mkdir(exist_ok=True)
for index, ref in enumerate(response.artifacts or ()):
extension = guess_extension(ref.mime_type) or ".bin"
path = destination / f"file-{index}{extension}"
if ref.custody == "vault_private":
client.private_artifacts.download_to(
ref, path,
session_id=response.session_id,
)
else:
client.artifacts.download_to(ref, path)Choose the destination filename and extension in your application. Downloads replace the destination only after successful verification. Async callers use adownload_to. Private file bytes move between your application and the same base_url as every other call. A private reference contains a safe label, not the original private filename or file contents. mAIvn's hosted managed vault is not part of the current release.
For multi-turn edits, obtain the exact revision's portable source with client.artifacts.download_source(ref, session_id=...) or client.private_artifacts.download_source(ref, session_id=...). Pass those bytes and the reference to the matching Maivn toolset's resume_artifact or resume_private_artifact. Rendered updates carry their expected base revision. The server rejects a stale base and preserves prior downloads. This restores Maivn composition sources, including embedded images. It is not an arbitrary Office-file importer. Use the authenticated SDK session for the current thread, not a Studio UI session identifier.
Custom toolsets can return maivn.files.generated_file(path) with a GeneratedFile return annotation and an authorized output directory. New file formats use the generic attachment route, preserving their filename and bytes without new SDK, Studio, or server format adapters. Known document and image formats keep their specialized checks; other formats download as application/octet-stream with no inline preview. The same helper accepts private custody, optional versioned custom source bytes, and an exact prior reference for revisions. The application owns its local output directory and source editor. Durable private output requires a compatible private-file service and its retention policy. It is separate from the SDK's local private-data store.
Private deletion supports client.private_artifacts.delete(ref, session_id=...). An explicit include_redacted_exports=True also selects retained redacted derivatives. export_redacted(ref, session_id=..., filename=...) is available only where the Vault has an authentic redacted source. Newly ingested private files do not implicitly have one. Existing canonical-work calls remain supported and reject extra filename, session, or deletion-scope overrides.
Private publication requires a configured retention policy. SDK-local generated files support private intake. External webhook tool callbacks currently refuse private artifact references.
#Durable threads and interrupts
client.thread(thread_id=None, *, options=None) returns a BoundThread. Construction is local. Omitting the ID selects a fresh one. The same factory exists on Agent and Swarm and preserves their execution defaults and tools.
conversation = client.thread()
response = conversation.send(["Remember the project is Atlas.", "Summarize it."])
response = conversation.send("What is the project called?")The handle offers send / asend, stream / astream, state / astate, and events / aevents. Sending returns the ordinary InvokeResponse through the normal invocation path. Streams retain the last observed replay position for the active turn. See thread handles and cleanup.
Use start_thread(), post_thread_message(), get_thread(), thread_events(), and their async forms for durable execution.
The dispatch methods start_thread, post_thread_message, and time_travel_thread accept options, tools, private_data, force_final_tool, and swarm_members, with matching async signatures. Their return value is an acceptance receipt. Raw thread_events consumption does not service local Python tools. Use normal invocation or a bound handle for that execution lifecycle. Continuation and time-travel dispatch require one message. Initial dispatch and normal invocation support full message lists.
Approval and question interrupts are resumed explicitly:
from maivn import ApprovalDecision, Client
client = Client(api_key="...")
accepted = client.start_thread("Prepare the release")
client.submit_approval(
accepted.thread_id,
"interrupt-123",
decision=ApprovalDecision(
approved=True,
decided_by="operator@example.com",
),
)
client.submit_interrupt_response(
accepted.thread_id,
"interrupt-456",
answer="Use the staged rollout",
responded_by="operator@example.com",
)cancel() cancels a runtime session. Thread time travel is available through time_travel_thread() and time_travel_thread_stream() plus their async equivalents.
#Deliberately absent operations
The current public client has no get_usage() or preview_redaction() method. Billing and redaction-preview request/response classes are exported read models. Do not present them as callable SDK operations until a public transport method exists.