Skip to main content

saperly

Python SDK for the Saperly v2 API — the camelCase data plane (numbers, connections, messaging, voice, usage, consent, pricing, keys).

Autogenerated from the OpenAPI contract.

Install

pip install saperly        # or: uv add saperly

Requires Python 3.9+. The async API needs no extra install (uses httpx).

Usage

from saperly import create_client
from saperly.api.numbers import numbers_list, numbers_provision
from saperly.api.voice import voice_place

client = create_client(api_key="sap_sk_live_...")

provisioned = numbers_provision.sync(client=client, body=...)
calls = numbers_list.sync(client=client)

Each operation module exposes four entry points:

  • sync(...) — returns the parsed body (or None).
  • sync_detailed(...) — returns a Response with status_code, headers, parsed.
  • asyncio(...) / asyncio_detailed(...) — the async equivalents.

Convenience wrapper

Saperly holds a configured client so you don't thread base_url / token through every call:

from saperly import Saperly
from saperly.api.usage import usage_summary

with Saperly(api_key="sap_sk_live_...") as saperly:
    summary = usage_summary.sync(client=saperly.client)

Retries

create_client() / Saperly(...) retry idempotent requests (GET/HEAD/OPTIONS/DELETE) once on 5xx + connection errors. POST/PATCH are never retried. Tune or disable with retries:

client = create_client(api_key="sap_sk_live_...", retries=3)  # or retries=0

Webhooks

Verify the signature on inbound Saperly webhooks. Saperly signs each delivery x-saperly-signature: v1=<hex> (HMAC-SHA256 over f"{timestamp}.{body}") with x-saperly-timestamp; verify_webhook checks the signature constant-time, then the timestamp window (default 5 min).

from saperly import verify_webhook

result = verify_webhook(raw_body, secret, request.headers)
if not result.valid:
    return Response(status_code=400)
# Dedup result.delivery_id for >= the tolerance window to defeat replays.

Agent brain (manual mode)

In manual mode a connection's "brain" drives a live phone call: Saperly POSTs a signed {"event": ...} to the connection's webhook per call event and reads back exactly ONE directive. saperly.agent turns that into a typed handler surface — you register per-event handlers, the framework owns signature verification (401 on a bad signature), parsing (400 on a malformed event), dispatch, and the fail-safe rule (past the signature gate it always returns 200 with a graceful directive, since a 5xx degrades a live call). Stdlib only (no pydantic, no extra dependency).

from fastapi import FastAPI
from saperly.agent import AgentBrain, accept, Reject

brain = AgentBrain(secret=SAPERLY_MANUAL_SECRET)  # the connection's manualSecret

@brain.on_inbound_call
def open_call(event):
    return accept(greeting=f"Hi, you've reached {event.to}. How can I help?")

@brain.on_turn
def turn(event):
    return f"You said: {event.user_text}"  # a str → Speak(...)

app = FastAPI()
app.mount("/manual", brain.asgi())  # or: brain.handler(raw_body, headers) -> (status, body)

A handler returns a directive (Speak, Reject, Transfer, …), a str (shorthand for Speak), or None (a safe default). brain.asgi() mounts on FastAPI/Starlette with no extra dependency; brain.handler(raw_body, headers) is the sync framework-agnostic entry (brain.handle_async(...) from async code).

Auth

Scoped Saperly API key (sap_sk_live_…), sent as Authorization: Bearer <key> (handled by AuthenticatedClient). Mint keys in the dashboard.

Development

This package is generated — do not edit saperly/client.py, saperly/api/, or saperly/models/. The hand-written facade is saperly/__init__.py + saperly/_saperly.py, preserved across regeneration.

bash scripts/generate.sh          # regenerate from ../openapi.v2.json
uv run --no-project --with httpx --with attrs --with python-dateutil --with pytest \
  python -m pytest tests/          # smoke tests

The generator is openapi-python-client (run via uvx); config in openapi-python-client.yaml. The contract source of truth is the SaperlyApi Effect HttpApi in packages/api.

Release files for saperly 3.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for saperly 3.0.0
File Size Uploaded
saperly-3.0.0.tar.gz 74.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for saperly 3.0.0
File Interpreter ABI Platform
saperly-3.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 306.7 kB

Release files / saperly-3.0.0.tar.gz

Download URL saperly-3.0.0.tar.gz
Size 74.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d639191dd585029206666c2ee83775e6cec4f0191661fbf40396a7191247fcb3
BLAKE2b-256 checksum
How to use checksums
8f2e7206791171e57bbc7a59ddbb8322d605a314eea5c7e1150c593238bb1e88
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Release files / saperly-3.0.0-py3-none-any.whl

Download URL saperly-3.0.0-py3-none-any.whl
Size 231.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c3e910f7d8f93ff7afc3ce1165179bd4a15655bdb1202877f5002618ba4db35f
BLAKE2b-256 checksum
How to use checksums
6dc0f16e41477b617d2fcd5739eea2f38e0b0c95de8dbea6af430c4d60c45c37
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Release history Release notifications | RSS feed

This release

3.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page