Official Python SDK for the Safwatech OTP / verification platform — OTP (push / SMS / email / WhatsApp), TOTP authenticator (RFC 6238) with backup codes, magic links, silent push approval, and WebAuthn / passkeys.
pip install safwatech-otpRequires Python ≥ 3.9. Single runtime dependency: requests.
from safwatech_otp import OTPClient
client = OTPClient(
api_key="sk_test_…", # or sk_live_…
base_url="https://otp.safwatech.com.ly",
)
sent = client.send_otp("+218911234567", channel="sms", reference_id="order-42")
# When using a sandbox key, the response includes the otp_code so you can
# round-trip without hitting a real SMS provider:
if code := sent["result"].get("otp_code"):
client.verify_otp("+218911234567", code, reference_id="order-42")API keys are issued from the client portal. sk_test_* keys put the call in
sandbox mode — no real delivery, no quota consumption, responses are
stamped with X-Sandbox: true.
All endpoints return a JSON envelope; OTPClient returns the decoded body
verbatim so you can read the platform's payload structure directly:
{
"status": "success",
"result": {...} # endpoint-specific
}Errors raise OTPClientError with the HTTP status, decoded payload, and
the Retry-After value when present:
from safwatech_otp import OTPClientError
try:
client.send_otp("+218911234567")
except OTPClientError as exc:
if exc.status == 429:
time.sleep(exc.retry_after or 1)
...| Surface | Methods |
|---|---|
| OTP | send_otp · verify_otp · get_status · usage |
| Scheduled | schedule_otp · cancel_scheduled · list_scheduled |
| TOTP | enroll_totp · verify_totp · totp_status · unenroll_totp · generate_backup_codes |
| Magic links | send_magic_link · get_magic_link · cancel_magic_link · wait_for_magic_link |
| Silent push auth | request_auth · get_auth_request · cancel_auth_request · wait_for_auth |
| WebAuthn / passkeys | webauthn_register · webauthn_authenticate · get_webauthn · cancel_webauthn · wait_for_webauthn |
The wait_for_* helpers poll the matching get_* endpoint until the request
reaches a terminal state (e.g. consumed | expired | cancelled for magic
links) or the timeout elapses; they return the final polled response.
from safwatech_otp import verify_webhook
if not verify_webhook(
raw_body=request.body,
secret="the callback_secret you set on your service",
signature_header=request.headers.get("X-Webhook-Signature", ""),
timestamp_header=request.headers.get("X-Webhook-Timestamp", ""),
):
abort(401)The header format is X-Webhook-Signature: v1=<hex> over "{timestamp}.{body}"
(HMAC-SHA256). A 300 s tolerance window is enforced by default.
- Live API reference: https://otp.safwatech.com.ly/docs
- Runnable scripts:
sdks/examples/in the source repo (python_example.py,magic_link.py,silent_auth.py,totp_enroll.py,webauthn.py,verify_webhook.py).
pip install -e ".[test]"
pytestMIT. See LICENSE.