SPAPS Python Client
The Python client distribution name is spaps, and the import path is spaps_client.
Verified source surface: spaps 0.7.1 at Sweet Potato
5b09d3df1194577c5313c865fef1db98f694cdea, with Python 3.9 or newer. The API described below
comes from packages/python-client/src/spaps_client; an installed or published package may lag the
checkout.
Install vs Import
pip install spaps
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
from spaps_client import MfaRequiredChallenge, SpapsClient
client = SpapsClient(
base_url="http://localhost:3301",
# Built-in key for local mode only; never deploy or reuse it in production.
api_key="spaps_local_development_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
from spaps_client import AsyncSpapsClient, MfaRequiredChallenge
async def main() -> None:
client = AsyncSpapsClient(
base_url="http://localhost:3301",
# Built-in key for local mode only; use a provisioned secret key elsewhere.
api_key="spaps_local_development_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.
from spaps_client import DeviceFlowClient
flow = DeviceFlowClient(
base_url="https://api.example.test",
client_id="application-slug",
api_key="spaps_pub_example",
)
# 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
From the monorepo root:
npm run lint:python-client
npm run typecheck:python-client
npm run test:python-clientFrom packages/python-client:
pip install -e '.[dev]'Pair this page with Issue reporting and support and Billing and entitlements for backend contract details.