Python SDK
Complete reference for the slootly Python package.
Installation
bash
pip install slootlyInitialization
Simple (API key only)
python
from slootly import Slootly
gate = Slootly("sk_live_your_api_key")Full Configuration
python
from slootly import Slootly
gate = Slootly(
"sk_live_your_api_key",
base_url="https://api.slootly.dev/v1", # default
cache_ttl=300, # 5 minutes in seconds, default
offline_verification=True, # default
timeout=5.0, # 5 seconds, default
checkout_base_url="https://pay.slootly.dev", # default
)Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key | str | (required) | API key starting with sk_live_ or sk_test_ |
base_url | str | https://api.slootly.dev/v1 | Slootly API base URL |
cache_ttl | int | 300 | In-memory cache TTL in seconds |
offline_verification | bool | True | Enable JWT offline verification |
timeout | float | 5.0 | HTTP request timeout in seconds |
checkout_base_url | str | https://pay.slootly.dev | Hosted checkout base URL |
Async Context Manager
python
async with Slootly("sk_live_...") as gate:
sub = await gate.check(user_id, "telegram")
# Resources are cleaned up automatically on exitgate.check()
Check subscription status for a user on a platform. This is the primary SDK method. Checks in order: in-memory cache, JWT offline verification, then API call.
signaturepython
async def check(self, user_id: str, platform: str) -> SubscriptionParameters
| Parameter | Type | Description |
|---|---|---|
user_id | str | Platform-specific user ID (e.g., Telegram from.id as string) |
platform | str | 'telegram' | 'discord' | 'whatsapp' | 'slack' | 'custom' |
Returns: Subscription
| Property | Type | Description |
|---|---|---|
active | bool | Whether the user has an active subscription |
plan | Optional[str] | Plan slug (e.g., 'pro'). None if no subscription |
plan_id | Optional[str] | Plan ID |
status | str | 'active' | 'trialing' | 'past_due' | 'canceled' | 'expired' | 'none' |
current_period_start | Optional[datetime] | Current billing period start |
current_period_end | Optional[datetime] | Current billing period end |
cancel_at_period_end | bool | Whether the sub will cancel at period end |
trial_end | Optional[datetime] | Trial end date. None if not on trial |
subscriber_id | Optional[str] | Slootly subscriber ID |
platforms | List[str] | All platforms linked to this subscriber |
metadata | Dict[str, Any] | Developer-defined metadata |
Example
python
sub = await gate.check("123456789", "telegram")
if sub.active:
print(f"Plan: {sub.plan}") # "pro"
print(f"Status: {sub.status}") # "active"
print(f"Expires: {sub.current_period_end}")
print(f"Platforms: {sub.platforms}") # ["telegram", "discord"]
else:
print("No active subscription")gate.checkout_url()
Generate a checkout URL for a user to subscribe to a plan. This is a synchronous method -- no API call needed.
signaturepython
def checkout_url(
self,
user_id: str,
plan_slug: str,
platform: str,
*,
success_url: Optional[str] = None,
cancel_url: Optional[str] = None,
email: Optional[str] = None,
metadata: Optional[Dict[str, str]] = None,
) -> strParameters
| Parameter | Type | Description |
|---|---|---|
user_id | str | Platform-specific user ID |
plan_slug | str | Plan slug (e.g., 'pro') |
platform | str | Platform name |
success_url | Optional[str] | Custom success redirect URL |
cancel_url | Optional[str] | Custom cancel redirect URL |
email | Optional[str] | Pre-fill customer email |
metadata | Optional[Dict[str, str]] | Additional metadata |
Example
python
# Simple
url = gate.checkout_url("123456789", "pro", "telegram")
# => https://pay.slootly.dev/checkout/pro?uid=123456789&platform=telegram
# With options
url = gate.checkout_url(
"123456789", "pro", "telegram",
success_url="https://t.me/mybot",
email="user@example.com",
metadata={"referral": "homepage"},
)gate.get_plans()
Fetch all active plans for the project.
signaturepython
async def get_plans(self) -> List[Plan]Plan Dataclass
| Property | Type | Description |
|---|---|---|
id | str | Plan ID |
name | str | Display name |
slug | str | URL-safe identifier |
description | Optional[str] | Plan description |
amount | int | Price in smallest currency unit (cents) |
currency | str | Currency code (e.g., 'usd') |
interval | str | 'day' | 'week' | 'month' | 'year' |
interval_count | int | Billing interval count |
trial_days | int | Free trial days |
features | List[str] | Feature list |
metadata | Dict[str, Any] | Custom metadata |
Example
python
plans = await gate.get_plans()
for plan in plans:
print(f"{plan.name}: ${plan.amount / 100}/{plan.interval}")
print(f"Features: {', '.join(plan.features)}")gate.cancel_subscription()
Cancel a user's subscription. The subscription remains active until the end of the current billing period.
python
sub = await gate.check("123456789", "telegram")
if sub.subscriber_id:
await gate.cancel_subscription(sub.subscriber_id)
# Subscription remains active until sub.current_period_endgate.verify_webhook()
Verify a webhook signature and parse the event.
signaturepython
def verify_webhook(
self,
payload: str, # raw request body
signature: str, # Slootly-Signature header
secret: str, # webhook secret (whsec_...)
) -> WebhookEventpython
# FastAPI example
from fastapi import Request, Header
@app.post("/webhooks/slootly")
async def handle_webhook(
request: Request,
slootly_signature: str = Header(..., alias="Slootly-Signature"),
):
payload = await request.body()
event = gate.verify_webhook(
payload.decode(),
slootly_signature,
"whsec_your_webhook_secret",
)
if event.type == "subscription.created":
print(f"New subscriber: {event.data.subscriber_id}")
elif event.type == "payment.failed":
print(f"Payment failed: {event.data.subscriber_id}")
return {"ok": True}WebhookEvent Type
| Property | Type | Description |
|---|---|---|
id | str | Unique event ID |
type | str | Event type (e.g., 'subscription.created') |
created_at | datetime | Event timestamp |
project_id | str | Project ID |
data | WebhookEventData | Event payload |
WebhookEventData
| Property | Type | Description |
|---|---|---|
subscriber_id | Optional[str] | Subscriber ID |
subscription_id | Optional[str] | Subscription ID |
plan | Optional[Dict[str, str]] | Plan info (id, name, slug) |
status | Optional[str] | Subscription status |
platform | Optional[str] | Platform name |
platform_user_id | Optional[str] | Platform user ID |
current_period_start | Optional[str] | Period start (ISO 8601) |
current_period_end | Optional[str] | Period end (ISO 8601) |
cancel_at_period_end | Optional[bool] | Cancel at period end flag |
metadata | Optional[Dict[str, Any]] | Custom metadata |
Cleanup
python
# Clear all cached subscription data
gate.clear_cache()
# Clean up SDK resources (close HTTP sessions)
await gate.close()
# Or use async context manager for automatic cleanup
async with Slootly("sk_live_...") as gate:
sub = await gate.check(user_id, "telegram")Error Handling
python
from slootly import Slootly
from slootly.errors import ValidationError, AuthenticationError, NetworkError
try:
sub = await gate.check(user_id, "telegram")
except ValidationError as e:
# Invalid input (empty user_id, invalid platform)
print(f"Validation error: {e}")
except AuthenticationError as e:
# Invalid API key
print(f"Auth error: {e}")
except NetworkError as e:
# Network issue, API unreachable
print(f"Network error: {e}")Info
The SDK gracefully handles network failures. If the API is unreachable and a cached JWT is available, the SDK falls back to JWT verification. Your bot continues working even if Slootly is temporarily down.