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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

saperly-3.0.0.tar.gz (74.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

saperly-3.0.0-py3-none-any.whl (231.8 kB view details)

Uploaded Python 3

File details

Details for the file saperly-3.0.0.tar.gz.

File metadata

  • Download URL: saperly-3.0.0.tar.gz
  • Upload date:
  • Size: 74.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for saperly-3.0.0.tar.gz
Algorithm Hash digest
SHA256 d639191dd585029206666c2ee83775e6cec4f0191661fbf40396a7191247fcb3
MD5 30587f76059aae487baedca1a29a7821
BLAKE2b-256 8f2e7206791171e57bbc7a59ddbb8322d605a314eea5c7e1150c593238bb1e88

See more details on using hashes here.

File details

Details for the file saperly-3.0.0-py3-none-any.whl.

File metadata

  • Download URL: saperly-3.0.0-py3-none-any.whl
  • Upload date:
  • Size: 231.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for saperly-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c3e910f7d8f93ff7afc3ce1165179bd4a15655bdb1202877f5002618ba4db35f
MD5 a9aa1d31253e7107d4702db1e9132090
BLAKE2b-256 6dc0f16e41477b617d2fcd5739eea2f38e0b0c95de8dbea6af430c4d60c45c37

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page