Skip to main content

Install

pip install yotel
Requires Python 3.10+.

Quickstart

import os
import yotel

client = yotel.Client(api_key=os.environ["YOTEL_KEY"])

# Create campaign
c = client.campaigns.create(
    name="Q4 outreach",
    dial_mode="predictive",
    predictive_target_abandon=0.02,
)

# Push lead
lead = client.leads.create(
    campaign_id=c.id,
    phone="9876543210",
    name="Priya",
    email="priya@example.com",
)

# Bulk push (up to 500)
result = client.leads.bulk(
    campaign_id=c.id,
    leads=[
        {"phone": "9876543211"},
        {"phone": "9876543212", "name": "Arjun"},
    ],
)
print(result.valid, "added,", result.duplicates, "skipped")

Verifying webhooks

from yotel.webhook import verify_and_parse, InvalidSignature

# In your FastAPI / Flask / Django handler:
raw = await request.body()
try:
    event = verify_and_parse(
        raw,
        headers=dict(request.headers),
        signing_secret=os.environ["YOTEL_WEBHOOK_SECRET"],
    )
except InvalidSignature:
    return Response(status_code=401)

# event.event_type is typed: 'call.ended' | 'campaign.paused' | ...
if event.event_type == "call.ended":
    call_id = event.data["call_id"]
    ...

Rate-limit handling

By default the client retries 429s up to 3 times, respecting Retry-After:
# Default — let the SDK retry
client = yotel.Client(api_key="...")

# Disable — handle 429 yourself
client = yotel.Client(api_key="...", retry_on_429=False)

Async

import yotel

async with yotel.AsyncClient(api_key="...") as client:
    c = await client.campaigns.create(name="...", dial_mode="predictive")
Both sync and async clients ship in the same package.

Error handling

from yotel import (
    AuthenticationError,
    RateLimitError,
    ValidationError,
    APIError,
)

try:
    client.leads.create(campaign_id="c-1", phone="bad")
except ValidationError as e:
    print(e.errors)  # Pydantic-shaped field errors
except AuthenticationError:
    ...
except RateLimitError as e:
    print("retry after", e.retry_after)
except APIError as e:
    print(e.status_code, e.detail)

OAuth helpers

For partner apps that act on behalf of individual Yotel users, the SDK ships PKCE-ready OAuth helpers in yotel.oauth:
from yotel.oauth import AuthorizationCodeFlow, generate_code_verifier

flow = AuthorizationCodeFlow(
    client_id="sfdc_yotel",
    client_secret=os.environ["YOTEL_CLIENT_SECRET"],
    redirect_uri="https://app.example/cb",
    scopes=["campaigns:read", "openid", "offline_access"],
)
verifier = generate_code_verifier()
url = flow.build_authorize_url(state="csrf-1", code_verifier=verifier)
# ... redirect user, callback handler ...
tokens = flow.exchange_code(code=code, code_verifier=verifier)
tokens = tokens.refresh()  # rotated refresh_token
For the server-to-server JWT Bearer grant, install the oauth extra:
pip install "yotel[oauth]"
See OAuth 2.0 for the full flow reference.

Source

github.com/kamal-zetta/dialer/tree/main/sdks/python