Python SDK
The midwater package is a typed client for the Midwater API with sync and async clients, built on httpx. It supports Python 3.10 and later. Source: midwater-sdk-python.
Install
Section titled “Install”pip install midwaterVersion 0.1.0, on PyPI. Each release is built and published by the repository’s release workflow, with a signed provenance attestation.
Create a client
Section titled “Create a client”Both the API key and the base URL are required. There’s no default host.
from midwater import Midwater
client = Midwater() # reads MIDWATER_API_KEY and MIDWATER_BASE_URL# or: Midwater(api_key=..., base_url=..., timeout=30.0, max_retries=2)client.close()Use it as a context manager, or call client.close(), to release connections.
Send and read conversations
Section titled “Send and read conversations”accepted = client.conversations.create( { "external_id": "call_8f2a91", "channel": "voice", "agent": {"id": "front-desk", "name": "Front desk", "version": "3.2.0"}, "transcript": [ {"speaker": "agent", "text": "Thanks for calling. How can I help?"}, {"speaker": "user", "text": "I need to move my appointment to Thursday."}, ], }, idempotency_key="call_8f2a91", # optional; one is generated per call when omitted)print(accepted.id, accepted.status, accepted.duplicate, accepted.replayed)
conversation = client.conversations.wait(accepted.id, timeout=60)print(conversation.outcome)for result in conversation.results: print(result.check_key, result.verdict, result.score, result.reason)
client.conversations.feedback("call_8f2a91", "appointment_not_completed", "fail", note="The booking failed.")Payloads are TypedDicts (ConversationCreate, Turn, Event), so your editor checks them. Responses are dataclasses with attribute access; each keeps the full JSON in .raw.
Health
Section titled “Health”agent = client.agents.health("front-desk")print(agent.health_status, agent.reason, agent.last_7_days.resolution_rate)group = client.groups.health("brightsmile-dental")from midwater import AsyncMidwater
async with AsyncMidwater() as client: accepted = await client.conversations.create(payload) conversation = await client.conversations.wait(accepted.id)Errors and retries
Section titled “Errors and retries”Errors subclass MidwaterError. API errors have .status, .type, .message, .fields, .body and .request_id; there’s a class for each status in Errors, and unknown statuses map by class. APIConnectionError means no answer; WaitTimeoutError means wait gave up.
from midwater import ValidationError
try: client.conversations.create({"external_id": "x", "channel": "voice", "transcript": []})except ValidationError as e: print(e.fields, e.request_id)The client retries 408, 429, 5xx and connection errors on calls that are safe to repeat (GETs and conversations.create), at most 3 times, with exponential backoff, honouring Retry-After. Every POST sends an idempotency key; feedback isn’t retried until the API honours keys there.
Webhooks
Section titled “Webhooks”from midwater import verify_webhook, WebhookVerificationError
event = verify_webhook(raw_body, request.headers, secret) # raw bytes, exactly as receivedSee Verifying signatures.