Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/useautumn/autumn/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Autumn Python SDK provides a type-safe interface for managing customers, subscriptions, usage tracking, and billing. This guide covers common use cases and patterns.

Quick Start

from autumn_sdk import Autumn
import os

# Initialize the client
client = Autumn(secret_key=os.getenv("AUTUMN_SECRET_KEY"))

# Get or create a customer
customer = client.customers.get_or_create(
    customer_id="user_123",
    name="John Doe",
    email="john@example.com"
)

# Check if customer has access to a feature
result = client.check(
    customer_id="user_123",
    feature_id="messages"
)

if result.allowed:
    print("Access granted!")
else:
    print("Access denied - upgrade required")

Customer Management

Get or Create a Customer

The get_or_create method is idempotent and updates customer data if they already exist:
customer = client.customers.get_or_create(
    customer_id="user_123",  # Your internal user ID
    name="John Doe",
    email="john@example.com",
    metadata={"source": "web", "plan": "trial"},
    fingerprint="device_abc123",  # For fraud prevention
    create_in_stripe=True,  # Create Stripe customer
    auto_enable_plan_id="plan_free",  # Auto-activate free plan
    send_email_receipts=True
)

print(f"Customer ID: {customer.id}")
print(f"Stripe ID: {customer.stripe_id}")

List Customers

from autumn_sdk.models import CustomerExpand

# List all customers with pagination
response = client.customers.list(
    limit=50,
    starting_after="cus_xyz",  # For pagination
    expand=[CustomerExpand.BALANCES, CustomerExpand.SUBSCRIPTIONS]
)

for customer in response.data:
    print(f"{customer.name} - {customer.email}")

Update a Customer

updated = client.customers.update(
    id="cus_123",
    name="John Smith",
    email="john.smith@example.com",
    metadata={"vip": True}
)

Delete a Customer

client.customers.delete(id="cus_123")

Usage Tracking

Check Feature Access

Check if a customer can access a feature before allowing the action:
# Simple check
result = client.check(
    customer_id="user_123",
    feature_id="api_calls"
)

if result.allowed:
    # Perform the action
    print(f"Remaining balance: {result.balance}")
else:
    print("Upgrade required")
    if result.preview:
        print(f"Suggested plan: {result.preview.recommended_plan}")

Check and Track Atomically

Combine checking and tracking in a single request for atomic operations:
# Check access and consume balance in one call
result = client.check(
    customer_id="user_123",
    feature_id="api_calls",
    required_balance=5,  # Require at least 5 credits
    send_event=True,  # Also track the usage
    properties={"endpoint": "/api/generate", "tokens": 150}
)

if result.allowed:
    print(f"Success! New balance: {result.balance}")
else:
    print(f"Insufficient balance. Current: {result.balance}")

Track Usage

Record usage after an action occurs:
# Track by feature ID
result = client.track(
    customer_id="user_123",
    feature_id="api_calls",
    value=1,  # Amount to consume (defaults to 1)
    properties={
        "endpoint": "/api/generate",
        "tokens": 150,
        "model": "gpt-4"
    }
)

print(f"Updated balance: {result.balance}")

# Track by event name (for multi-feature tracking)
result = client.track(
    customer_id="user_123",
    event_name="message_sent",  # Will update all features linked to this event
    value=1,
    properties={"message_type": "email"}
)

# Credit balance (negative value)
result = client.track(
    customer_id="user_123",
    feature_id="seats",
    value=-1,  # Remove a seat
    properties={"reason": "user_removed"}
)

Entity-Scoped Usage

Track usage scoped to specific entities (seats, projects, workspaces):
# Create an entity
entity = client.entities.create(
    customer_id="user_123",
    feature_id="seats",
    entity_id="workspace_abc",  # Your entity identifier
    name="Engineering Team"
)

# Track usage for the entity
result = client.check(
    customer_id="user_123",
    feature_id="seats",
    entity_id="workspace_abc"
)

# Delete entity when done
client.entities.delete(
    entity_id="workspace_abc",
    customer_id="user_123"  # Optional: scope to customer
)

Subscription Management

Attach a Plan

Subscribe a customer to a plan:
from autumn_sdk.models import AttachProrationBehavior

result = client.billing.attach(
    customer_id="user_123",
    plan_id="plan_pro_monthly",
    
    # For prepaid features (e.g., seats)
    feature_quantities=[
        {"feature_id": "seats", "quantity": 5}
    ],
    
    # Proration behavior
    proration_behavior=AttachProrationBehavior.PRORATE_IMMEDIATELY,
    
    # Apply discounts
    discounts=[
        {"type": "coupon", "id": "SUMMER20"}  # 20% off coupon
    ],
    
    # For payment method collection
    success_url="https://app.example.com/billing/success"
)

if result.checkout_url:
    print(f"Redirect user to: {result.checkout_url}")
else:
    print(f"Subscription activated: {result.subscription.id}")

Attach Multiple Plans

Subscribe to multiple plans in a single subscription:
result = client.billing.multi_attach(
    customer_id="user_123",
    plans=[
        {
            "plan_id": "plan_base",
            "feature_quantities": [{"feature_id": "users", "quantity": 10}]
        },
        {
            "plan_id": "addon_analytics"
        },
        {
            "plan_id": "addon_api",
            "feature_quantities": [{"feature_id": "api_calls", "quantity": 100000}]
        }
    ],
    success_url="https://app.example.com/billing/success"
)

Preview Plan Changes

Show customers the cost before making changes:
# Preview attaching a plan
preview = client.billing.preview_attach(
    customer_id="user_123",
    plan_id="plan_enterprise",
    feature_quantities=[{"feature_id": "seats", "quantity": 20}]
)

print(f"Immediate charge: ${preview.immediate_total / 100}")
print(f"Next invoice total: ${preview.next_total / 100}")

for item in preview.invoice_items:
    print(f"  {item.description}: ${item.amount / 100}")

Update a Subscription

from autumn_sdk.models import BillingUpdateAction

result = client.billing.update(
    customer_id="user_123",
    
    # Update prepaid quantities
    feature_quantities=[
        {"feature_id": "seats", "quantity": 10}  # Increase to 10 seats
    ],
    
    # Or cancel the subscription
    action=BillingUpdateAction.CANCEL,
    cancel_at_period_end=True  # Cancel at end of billing period
)

Customer Portal

Create a session for customers to manage their billing:
portal = client.billing.open_customer_portal(
    customer_id="user_123",
    return_url="https://app.example.com/settings/billing"
)

print(f"Redirect to: {portal.url}")

Setup Payment Method

session = client.billing.setup_payment(
    customer_id="user_123",
    success_url="https://app.example.com/billing/success",
    cancel_url="https://app.example.com/billing/cancel"
)

print(f"Redirect to: {session.url}")

Balance Management

Create a Balance

Manually create or grant balances:
balance = client.balances.create(
    customer_id="user_123",
    feature_id="api_calls",
    initial_balance=1000,  # Grant 1000 API calls
    reset_frequency="monthly",  # Reset every month
    expires_at="2025-12-31T23:59:59Z"  # Optional expiration
)

Update a Balance

balance = client.balances.update(
    balance_id="bal_xyz",
    balance=5000,  # Set new balance
    increment_by=1000  # Or increment by amount
)

Plans and Features

Create a Plan

from autumn_sdk.models import PlanInterval

plan = client.plans.create(
    id="plan_startup",
    name="Startup Plan",
    description="Perfect for growing teams",
    price=4900,  # $49.00 in cents
    interval=PlanInterval.MONTH,
    currency="usd",
    features=[
        {"feature_id": "seats", "included": 5, "unit_price": 900},  # $9 per extra seat
        {"feature_id": "api_calls", "included": 10000}
    ]
)

List Plans

plans = client.plans.list(limit=50)
for plan in plans.data:
    print(f"{plan.name}: ${plan.price / 100}/{plan.interval}")

Create a Feature

from autumn_sdk.models import FeatureType

feature = client.features.create(
    id="api_calls",
    name="API Calls",
    type=FeatureType.METERED,  # Or FEATURE_FLAG, SEAT, etc.
    unit_label="calls",
    description="Number of API calls per month"
)

List Features

features = client.features.list()
for feature in features.data:
    print(f"{feature.name} ({feature.type})")

Events and Analytics

List Usage Events

events = client.events.list(
    customer_id="user_123",  # Optional: filter by customer
    feature_id="api_calls",  # Optional: filter by feature
    limit=100,
    starting_after="evt_xyz"  # For pagination
)

for event in events.data:
    print(f"{event.created_at}: {event.feature_id} - {event.value}")

Aggregate Events

from autumn_sdk.models import AggregateBy, AggregateGroupBy

aggregates = client.events.aggregate(
    aggregate_by=AggregateBy.DAY,  # Group by day
    feature_id="api_calls",
    start_date="2024-01-01",
    end_date="2024-01-31",
    group_by=AggregateGroupBy.CUSTOMER  # Group by customer
)

for agg in aggregates.data:
    print(f"{agg.date}: {agg.total_usage} calls")

Referral Programs

Create a Referral Code

referral = client.referrals.create_code(
    customer_id="user_123",
    program_id="refer_friend"  # Your referral program ID
)

print(f"Referral code: {referral.code}")
print(f"Share link: {referral.share_url}")

Redeem a Referral Code

result = client.referrals.redeem_code(
    customer_id="user_456",  # New customer
    code="FRIEND20"  # Code from existing customer
)

print(f"Reward applied: {result.reward.description}")

Async/Await Support

All methods have async equivalents with _async suffix:
import asyncio
from autumn_sdk import Autumn

async def main():
    async with Autumn(secret_key="sk_live_...") as client:
        # All operations use async methods
        customer = await client.customers.get_or_create_async(
            customer_id="user_123",
            name="John Doe"
        )
        
        result = await client.check_async(
            customer_id="user_123",
            feature_id="messages"
        )
        
        if result.allowed:
            await client.track_async(
                customer_id="user_123",
                feature_id="messages",
                value=1
            )

asyncio.run(main())

Error Handling

The SDK raises specific exceptions for different error cases:
from autumn_sdk import Autumn, errors
import httpx

try:
    result = client.check(
        customer_id="user_123",
        feature_id="invalid_feature"
    )
except errors.AutumnError as e:
    # Base class for all Autumn API errors
    print(f"API Error: {e.message}")
    print(f"Status Code: {e.status_code}")
    print(f"Response Body: {e.body}")
    print(f"Headers: {e.headers}")
except httpx.TimeoutException:
    print("Request timed out")
except httpx.ConnectError:
    print("Failed to connect to API")
except errors.ResponseValidationError as e:
    # Response doesn't match expected schema
    print(f"Validation Error: {e.message}")
    print(f"Pydantic Error: {e.cause}")

Common Error Patterns

def safe_check_access(customer_id: str, feature_id: str) -> bool:
    """Safely check feature access with error handling."""
    try:
        result = client.check(
            customer_id=customer_id,
            feature_id=feature_id
        )
        return result.allowed
    except errors.AutumnError as e:
        if e.status_code == 404:
            # Customer or feature not found
            print(f"Not found: {e.message}")
            return False
        elif e.status_code == 429:
            # Rate limited
            print("Rate limited, try again later")
            return False
        else:
            # Other API error
            print(f"API error: {e.message}")
            raise
    except httpx.RequestError as e:
        # Network error
        print(f"Network error: {e}")
        return False

Best Practices

1. Use Context Managers

Always use with statements to ensure proper resource cleanup:
with Autumn(secret_key=os.getenv("AUTUMN_SECRET_KEY")) as client:
    # Your code here
    pass
# Connections automatically closed

2. Reuse Client Instances

Create one client instance and reuse it:
# ❌ Bad: Creating new clients
def check_access(user_id: str):
    client = Autumn(secret_key=os.getenv("AUTUMN_SECRET_KEY"))
    return client.check(customer_id=user_id, feature_id="messages")

# ✅ Good: Reuse client
client = Autumn(secret_key=os.getenv("AUTUMN_SECRET_KEY"))

def check_access(user_id: str):
    return client.check(customer_id=user_id, feature_id="messages")

3. Use Type Hints

The SDK includes full type hints for better IDE support:
from autumn_sdk.models import Customer, CheckResponse

def get_customer(customer_id: str) -> Customer:
    return client.customers.get_or_create(customer_id=customer_id)

def check_feature(customer_id: str, feature_id: str) -> CheckResponse:
    return client.check(customer_id=customer_id, feature_id=feature_id)

4. Handle Errors Gracefully

Always implement error handling for production code:
try:
    customer = client.customers.get_or_create(
        customer_id="user_123",
        email="user@example.com"
    )
except errors.AutumnError as e:
    logger.error(f"Failed to create customer: {e.message}")
    # Handle error appropriately
    raise

5. Use Environment-Specific Configuration

import os

# Different configs for different environments
if os.getenv("ENV") == "production":
    client = Autumn(
        secret_key=os.getenv("AUTUMN_SECRET_KEY_PROD"),
        timeout_ms=10000
    )
else:
    client = Autumn(
        secret_key=os.getenv("AUTUMN_SECRET_KEY_DEV"),
        timeout_ms=30000,
        debug_logger=logging.getLogger("autumn_sdk")
    )

Next Steps

API Reference

Explore the complete API documentation

Webhook Events

Learn how to handle webhook events

TypeScript SDK

Check out the TypeScript SDK

Examples

View example implementations