Python SDK
The rymi Python package wraps the Rymi REST API for server-side use. Use it from backend services, workers, and data pipelines that manage agents, calls, numbers, and webhook verification.
INFO
rymi is published on PyPI and is the supported server-side SDK for Python teams. For in-browser / WebRTC voice, use the Browser or React SDKs.
Installation
pip install rymiLatest release: rymi on PyPI.
Initialization
from rymi import Rymi
rymi = Rymi(api_key="rymi_your_secret_key")
# Or use the RYMI_API_KEY environment variable:
rymi = Rymi()Agents
agent = rymi.agents.create(
name="Customer Support",
system_prompt="You are a helpful customer support assistant for Acme Corp.",
voice="Aoede",
language="en-US",
)
agent_id = agent["id"]
agents = rymi.agents.list(limit=20)
options = rymi.agents.llm_options()create and update accept **kwargs that pass straight through to the REST body, so any documented agent field works, including multi-language and model-stack configuration:
bilingual = rymi.agents.create(
name="Ganga Sahayak",
language="hi-IN",
supported_languages=["hi-IN", "en-US"],
llm_provider="gemini",
llm_model="gemini-2.5-flash",
tts_provider="sarvam",
llm_fallback_provider="openai",
llm_fallback_model="gpt-4o-mini",
)Preview the model stack
preview = rymi.agents.preview_stack(
supported_languages=["hi-IN", "en-US"],
)
print(preview["blockers"], preview["warnings"]) # empty == good to saveKnowledge sources, history & evals
# Knowledge (RAG) — add from text or a URL
rymi.agents.add_knowledge_source(agent_id, kind="text", title="Refund policy",
text="Refunds are processed within 5 business days...")
sources = rymi.agents.list_knowledge_sources(agent_id)
rymi.agents.delete_knowledge_source(agent_id, sources["sources"][0]["id"])
# Configuration history — auditable, reversible
changes = rymi.agents.list_changes(agent_id)
rymi.agents.undo_change(agent_id, changes["changes"][0]["change_id"])
# Evaluations
run = rymi.agents.run_evals(agent_id, mode="synthetic")
runs = rymi.agents.list_eval_runs(agent_id)
detail = rymi.agents.get_eval_run(agent_id, run["id"])# Clone an agent
clone = rymi.agents.clone(agent_id)
# List calls for an agent
calls = rymi.agents.list_calls(agent_id, limit=20)
# Validate before publishing
report = rymi.agents.validate_publish(agent_id=agent_id)
# Apply a change-set (does not persist — follow up with update())
result = rymi.agents.apply_changes(
current_config={"name": "Support", "voice": "Aoede"},
changes=[{"key": "voice", "value": "Charon"}],
mode="edit"
)
rymi.agents.update(agent_id, **result["config"])
# Enrich company from website
info = rymi.agents.enrich_company("Acme Corp", "https://acme.example.com")
if info["enriched"]:
print(info["companyDescription"])Calls
call = rymi.calls.create(
agent_id=agent_id,
participants=[
{
"transport": "pstn",
"identity": "+15551234567",
"from_number": "+15559876543"
}
],
metadata={"customer_name": "Alice"},
variables={"customer_segment": "renewal"}
)
details = rymi.calls.retrieve(call["id"])
transcript = rymi.calls.transcript(call["id"])Control a live call
# Add a participant to a live call (warm transfer / conference)
rymi.calls.add_participants(call["id"], [
{"transport": "pstn", "identity": "+15557654321", "from_number": "+15559876543"}
])
# End the call
rymi.calls.end(call["id"])Batch Calls
batch = rymi.calls.batch(
agent_id=agent_id,
to=["+15551234567", "+15557654321"],
from_number="+15559876543",
variables={"batch_label": "renewal-q2"}
)
print(batch["batch_id"], batch["queued"])Do-Not-Call (DNC)
Numbers are normalized to E.164 server-side, so any input format is accepted. Outbound calls to blocked numbers are refused.
rymi.dnc.add(phone_number="+15551234567", reason="customer opt-out")
# Add up to 1000 at once — invalid numbers are skipped and returned
result = rymi.dnc.add_batch(phone_numbers=["+15551234567", "+15557654321"])
print(result["count"], result["invalid_count"], result["invalid"])
# Check up to 500 numbers without adding them (read-only)
check = rymi.dnc.check(phone_numbers=["+15551234567"])
print(check["blocked_count"])
entries = rymi.dnc.list(limit=50)
rymi.dnc.remove("+15551234567") # re-enables outbound to itSee the Compliance API for the underlying REST endpoints.
Numbers
rymi.numbers.register("+15559876543")
numbers = rymi.numbers.list()
rymi.numbers.attach(numbers["numbers"][0]["number"], agent_id)
# Detach + remove a number from the account
rymi.numbers.remove(numbers["numbers"][0]["number"])Telephony
status = rymi.telephony.status() # {"connected": ..., "provider": ...}
carrier_numbers = rymi.telephony.numbers()
# Connect / disconnect a carrier (requires carrier credentials)
rymi.telephony.connect(provider="twilio", auth_id="AC...", auth_token="...")
rymi.telephony.disconnect()Publishable keys
list_publishable returns prefixes only. create_publishable returns the full key once, so store it then. These use your secret key, so call them server-side only.
keys = rymi.keys.list_publishable() # prefixes only, never full secrets
created = rymi.keys.create_publishable(
agent_id=agent["id"],
label="Marketing site widget",
allowed_channels=["web"],
)
print(created["key"]) # sb_publishable_... — store now
rymi.keys.revoke_publishable(created["id"])Usage
Voice balance is reported in minutes (the customer-facing unit), not dollars.
usage = rymi.billing.usage_summary()
print(usage["voice_runtime"]["remaining_minutes"], usage["voice_runtime"]["status"])Cost estimate & spend controls
Spend-management settings are denominated in USD.
est = rymi.billing.estimate(
stt_model="nova-2",
llm_model="gemini-2.5-flash",
tts_model="gemini-2.5-flash-tts",
duration_seconds=300,
)
rymi.billing.set_auto_recharge(enabled=True, pack_usd=50, threshold_usd=10)
rymi.billing.set_alerts(thresholds_usd=[25, 50], low_balance_pct=15, email_enabled=True)Templates
templates = rymi.templates.list()["templates"]
starter = next((t for t in templates if t["label"] == "Healthcare Receptionist"), None)
if starter:
rymi.agents.create(name="Front Desk", **starter["defaults"])Webhooks
Register and maintain the endpoints Rymi delivers events to. Each endpoint has its own signing secret; keep it to verify deliveries (below).
created = rymi.webhooks.create(
url="https://api.example.com/rymi-webhook",
events=["call.completed", "call.intelligence.ready"],
secret="whsec_your_webhook_secret",
alert_email="oncall@example.com",
)
hooks = rymi.webhooks.list()
rymi.webhooks.update(hooks["webhooks"][0]["id"], events=["call.intelligence.ready"])
rymi.webhooks.delete(hooks["webhooks"][0]["id"])Verifying deliveries
verify_signature is a stateless helper; it needs no API key.
import json
from flask import Flask, request, jsonify
from rymi import Rymi
app = Flask(__name__)
rymi = Rymi()
@app.route("/webhook", methods=["POST"])
def handle_webhook():
signature = request.headers.get("x-rymi-signature")
timestamp = request.headers.get("x-rymi-timestamp")
payload_raw = request.get_data(as_text=True)
rymi.webhooks.verify_signature(
payload_raw,
signature,
timestamp,
"whsec_your_webhook_secret"
)
event = json.loads(payload_raw)
if event["type"] == "call.intelligence.ready":
print("Summary:", event["data"]["summary"])
return jsonify(status="OK"), 200
