Python SDK
The rymi Python package provides a server-side wrapper around the Rymi REST API. Use it from backend services, workers, and data pipelines that need to 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_role="operator"
)
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"],
agent_role="operator",
)
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 — 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(tier="operator", 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
