Skip to content

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.

Terminal window
pip install midwater

Version 0.1.0, on PyPI. Each release is built and published by the repository’s release workflow, with a signed provenance attestation.

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.

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.

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 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.

from midwater import verify_webhook, WebhookVerificationError
event = verify_webhook(raw_body, request.headers, secret) # raw bytes, exactly as received

See Verifying signatures.