SPAPS Python Client
The Python client distribution name is spaps, and the import path is spaps_client.
Verified public release: spaps 0.8.2, with Python 3.9 or
newer. The API below was checked against that release wheel. The upstream 0.8.3.dev0 checkout
is development source, not a published release.
Install vs Import
pip install spaps==0.8.2
python -c "from importlib.metadata import version; print(version('spaps'))"from spaps_client import SpapsClient, AsyncSpapsClientInstall name and import name are intentionally different: spaps (package) and spaps_client
(module).
Sync And Async Aggregate Clients
Both aggregate clients expose a grouped API surface and shared token helpers (set_tokens, get_tokens, clear_tokens):
| Client | Main grouped helpers |
|---|---|
SpapsClient | auth, sessions, payments, usage, whitelist, secure_messages, issue_reporting, metrics, support_telemetry |
AsyncSpapsClient | auth, sessions, payments, usage, whitelist, secure_messages, issue_reporting, metrics, entitlements, dayrate, users |
Sync Example
import os
from spaps_client import MfaRequiredChallenge, SpapsClient
client = SpapsClient(
base_url="https://api.sweetpotato.dev",
# Load a provisioned staging secret from server-side secret storage.
api_key=os.environ["SPAPS_API_KEY"],
)
try:
auth_result = client.auth.sign_in_with_password(
email="user@example.com",
password="correct-horse-battery-staple",
)
if isinstance(auth_result, MfaRequiredChallenge):
raise RuntimeError(f"Complete MFA challenge {auth_result.challenge_id} before API calls")
if auth_result.user is None:
raise RuntimeError("Sign-in returned tokens without a user projection")
session = client.sessions.get_current_session()
print(auth_result.user.email, session.session_id)
finally:
client.close()Async Example
import asyncio
import os
from spaps_client import AsyncSpapsClient, MfaRequiredChallenge
async def main() -> None:
client = AsyncSpapsClient(
base_url="https://api.sweetpotato.dev",
api_key=os.environ["SPAPS_API_KEY"],
)
try:
auth_result = await client.auth.sign_in_with_password(
email="user@example.com",
password="correct-horse-battery-staple",
)
if isinstance(auth_result, MfaRequiredChallenge):
raise RuntimeError(
f"Complete MFA challenge {auth_result.challenge_id} before API calls"
)
items = await client.issue_reporting.list_issue_reports(limit=10)
print(items.total)
finally:
await client.aclose()
asyncio.run(main())Helper Clients And Utilities
The package also exports narrower helpers when you do not want the aggregate clients:
| Helper | Purpose |
|---|---|
EntitlementsClient, AsyncEntitlementsClient | Entitlement list/check/claim/history and admin grant/revoke flows |
IssueReportingClient, AsyncIssueReportingClient | Issue status/history/create/update/reply and voice-token support |
EmailClient, AsyncEmailClient | Template list/preview/send |
UsersClient, AsyncUsersClient | Batch email and user lookup helpers |
DeviceFlowClient | OAuth device flow (/api/cli/device/authorize, /api/cli/device/token) |
PermissionChecker and related helpers | Role/admin permission checks |
verify_spaps_webhook | SPAPS webhook signature verification |
verify_crypto_webhook_signature | Crypto webhook signature verification |
Retry, Logging, And Token Storage
spaps_client.http exposes RetryConfig, LoggingHooks, and default_logging_hooks. Token persistence is available via TokenStorage implementations:
InMemoryTokenStorageFileTokenStorage(defaults to~/.config/spaps/tokens.json)
from spaps_client import (
FileTokenStorage,
RetryConfig,
SpapsClient,
default_logging_hooks,
)
client = SpapsClient(
base_url="https://api.example.test",
api_key="spaps_sec_example",
retry_config=RetryConfig(max_attempts=4, backoff_factor=0.2),
logging_hooks=default_logging_hooks(),
token_storage=FileTokenStorage(),
)Device Flow
DeviceFlowClient implements RFC 8628-style device authorization for CLI/headless flows.
It uses the provisioned application slug as client_id; these public-client requests do not send
an application API key.
from spaps_client import DeviceFlowClient
flow = DeviceFlowClient(
base_url="https://api.example.test",
client_id="application-slug",
)
# start() -> returns user_code + verification_uri
# poll() -> waits for token issuanceWebhook Verification
from spaps_client import verify_spaps_webhook
payload = verify_spaps_webhook(
body=request_body_bytes,
signature=request_headers["X-SPAPS-Signature"],
secret="whsec_example",
)
print(payload.type)Issue Reporting Surface
IssueReportingClient and AsyncIssueReportingClient expose:
get_issue_report_statuslist_issue_reportsget_issue_reportcreate_issue_reportupdate_issue_reportreply_to_issue_reportcreate_voice_token
Use create_voice_token for browser voice-input integrations so the browser gets a short-lived token from SPAPS instead of a long-lived provider secret.
Entitlements Surface
EntitlementsClient and AsyncEntitlementsClient expose:
get_user_entitlementscheck_accessclaim_pendingget_purchase_historyget_changesgrant_manualandrevoke(admin flows)
These helper clients are useful when you want entitlement logic outside the aggregate client surface.
Validation
Record the installed version with importlib.metadata.version('spaps') and retain your dependency
lock. Typecheck your application’s calls, then verify sign-in, session refresh, and an entitlement
decision against the provisioned staging application.
Pair this page with Issue reporting and support and Billing and entitlements for backend contract details.