docsSDKsPython

Python SDK

Complete Python examples for integrating with Lorn AI.


Installation

pip install requests

Setup

import os
import requests
from typing import Optional, List, Dict, Any
 
# Configuration
LORN_BASE_URL = os.getenv("LORN_BASE_URL", "https://{{YOUR_STORE_URL}}")
LORN_API_KEY = os.getenv("LORN_API_KEY", "{{YOUR_API_KEY}}")
 
class LornClient:
    """Lorn AI API client."""
    
    def __init__(self, base_url: str = None, api_key: str = None):
        self.base_url = base_url or LORN_BASE_URL
        self.api_key = api_key or LORN_API_KEY
        self.session = requests.Session()
        self.session.headers.update({
            "X-ACP-API-Key": self.api_key,
            "Accept": "application/json",
            "Content-Type": "application/json"
        })
    
    def _request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
        """Make an API request."""
        url = f"{self.base_url}{endpoint}"
        response = self.session.request(method, url, **kwargs)
        response.raise_for_status()
        return response.json()
    
    # Health
    def health(self) -> Dict[str, str]:
        """Check API health."""
        return self._request("GET", "/health")
    
    # Products
    def search_products(
        self,
        q: str = None,
        category: str = None,
        tag: str = None,
        min_price: float = None,
        max_price: float = None,
        page: int = 1,
        page_size: int = 10
    ) -> Dict[str, Any]:
        """Search for products."""
        params = {"page": page, "page_size": page_size}
        if q:
            params["q"] = q
        if category:
            params["category"] = category
        if tag:
            params["tag"] = tag
        if min_price is not None:
            params["min_price"] = min_price
        if max_price is not None:
            params["max_price"] = max_price
        
        return self._request("GET", "/acp/products", params=params)
    
    def get_product(self, product_id: str) -> Dict[str, Any]:
        """Get product by ID."""
        return self._request("GET", f"/acp/products/{product_id}")
    
    # Checkout
    def create_checkout(
        self,
        items: List[Dict[str, Any]],
        shipping_address: Dict[str, Any] = None,
        customer: Dict[str, Any] = None
    ) -> Dict[str, Any]:
        """Create a checkout session."""
        payload = {"items": items}
        if shipping_address:
            payload["shipping_address"] = shipping_address
        if customer:
            payload["customer"] = customer
        
        return self._request("POST", "/checkout_sessions", json=payload)
    
    def get_checkout(self, session_id: str) -> Dict[str, Any]:
        """Get checkout session."""
        return self._request("GET", f"/checkout_sessions/{session_id}")
    
    def update_checkout(
        self,
        session_id: str,
        items: List[Dict[str, Any]] = None,
        shipping_address: Dict[str, Any] = None,
        customer: Dict[str, Any] = None
    ) -> Dict[str, Any]:
        """Update checkout session."""
        payload = {}
        if items is not None:
            payload["items"] = items
        if shipping_address:
            payload["shipping_address"] = shipping_address
        if customer:
            payload["customer"] = customer
        
        return self._request("PATCH", f"/checkout_sessions/{session_id}", json=payload)
    
    def complete_checkout(self, session_id: str) -> Dict[str, Any]:
        """Complete checkout."""
        return self._request("POST", f"/checkout_sessions/{session_id}/complete")
    
    def cancel_checkout(self, session_id: str) -> Dict[str, Any]:
        """Cancel checkout."""
        return self._request("POST", f"/checkout_sessions/{session_id}/cancel")
    
    # Webhooks
    def emit_webhook(
        self,
        event_type: str,
        checkout_session_id: str = None,
        target_url: str = None,
        payload: Dict[str, Any] = None
    ) -> Dict[str, Any]:
        """Emit a webhook event."""
        body = {"event_type": event_type}
        if checkout_session_id:
            body["checkout_session_id"] = checkout_session_id
        if target_url:
            body["target_url"] = target_url
        if payload:
            body["payload"] = payload
        
        return self._request("POST", "/webhooks/emit", json=body)
 
 
# Create default client
client = LornClient()

Usage Examples

Search Products

# Basic search
results = client.search_products(q="comfortable running shoes")
for product in results["items"]:
    print(f"{product['title']} - ${product['price']}")
    if product.get("similarity_score"):
        print(f"  Relevance: {product['similarity_score']:.2f}")
 
# With filters
results = client.search_products(
    q="wireless headphones",
    min_price=50,
    max_price=200,
    page_size=5
)
 
# Category browse
results = client.search_products(category="Electronics", page_size=20)

Get Product Details

product = client.get_product("prod_abc123")
 
print(f"Title: {product['title']}")
print(f"Price: ${product['price']}")
print(f"Description: {product['description']}")
 
# Show variants
for variant in product["variants"]:
    attrs = ", ".join(f"{k}: {v}" for k, v in variant["attributes"].items())
    print(f"  - {variant['sku']}: {attrs} - ${variant['price']}")

Create Checkout

# Simple checkout
session = client.create_checkout(
    items=[
        {"product_id": "prod_abc123", "quantity": 1}
    ]
)
print(f"Session ID: {session['checkout_session']['id']}")
print(f"Total: ${session['checkout_session']['amounts']['total']}")
 
# With variant and full details
session = client.create_checkout(
    items=[
        {
            "product_id": "prod_abc123",
            "variant_sku": "sku_black_m",
            "quantity": 2
        }
    ],
    shipping_address={
        "name": "Jane Doe",
        "line1": "123 Main Street",
        "city": "San Francisco",
        "state": "CA",
        "postal_code": "94102",
        "country": "US"
    },
    customer={
        "email": "jane@example.com",
        "name": "Jane Doe"
    }
)

Update Checkout

# Update shipping address
session = client.update_checkout(
    session_id="cs_demo_abc123",
    shipping_address={
        "name": "Jane Doe",
        "line1": "456 Oak Avenue",
        "city": "Los Angeles",
        "state": "CA",
        "postal_code": "90001",
        "country": "US"
    }
)
 
# Update cart
session = client.update_checkout(
    session_id="cs_demo_abc123",
    items=[
        {"product_id": "prod_abc123", "quantity": 3}
    ]
)

Complete Checkout

order = client.complete_checkout("cs_demo_abc123")
print(f"Status: {order['checkout_session']['status']}")
print(f"Total: ${order['checkout_session']['amounts']['total']}")

Cancel Checkout

try:
    result = client.cancel_checkout("cs_demo_abc123")
    print(f"Canceled: {result['checkout_session']['status']}")
except requests.exceptions.HTTPError as e:
    if e.response.status_code == 405:
        print("Cannot cancel: already completed or canceled")
    else:
        raise

Complete Purchase Flow

def complete_purchase_flow():
    """Complete shopping flow example."""
    
    # 1. Search for products
    print("Searching for running shoes...")
    results = client.search_products(q="running shoes", max_price=150)
    
    if not results["items"]:
        print("No products found")
        return
    
    product = results["items"][0]
    print(f"Found: {product['title']} - ${product['price']}")
    
    # 2. Get product details
    product_details = client.get_product(product["id"])
    variant = product_details["variants"][0] if product_details["variants"] else None
    
    # 3. Create checkout
    print("\nCreating checkout...")
    items = [{"product_id": product["id"], "quantity": 1}]
    if variant:
        items[0]["variant_sku"] = variant["sku"]
    
    session = client.create_checkout(
        items=items,
        shipping_address={
            "name": "Jane Doe",
            "line1": "123 Main St",
            "city": "San Francisco",
            "state": "CA",
            "postal_code": "94102",
            "country": "US"
        },
        customer={"email": "jane@example.com"}
    )
    
    session_id = session["checkout_session"]["id"]
    amounts = session["checkout_session"]["amounts"]
    
    print(f"Session: {session_id}")
    print(f"Subtotal: ${amounts['subtotal']}")
    print(f"Tax: ${amounts['tax']}")
    print(f"Shipping: ${amounts['shipping']}")
    print(f"Total: ${amounts['total']}")
    
    # 4. Complete checkout
    print("\nCompleting purchase...")
    order = client.complete_checkout(session_id)
    
    print(f"Order completed!")
    print(f"Status: {order['checkout_session']['status']}")
    
    return order
 
 
if __name__ == "__main__":
    complete_purchase_flow()

Error Handling

import requests
 
def safe_api_call():
    """Example with error handling."""
    try:
        results = client.search_products(q="shoes")
        return results
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 401:
            print("Invalid API key")
        elif e.response.status_code == 404:
            print("Not found")
        elif e.response.status_code == 429:
            print("Rate limited - retry later")
        elif e.response.status_code >= 500:
            print("Server error - retry later")
        else:
            print(f"HTTP error: {e.response.status_code}")
        return None
    except requests.exceptions.ConnectionError:
        print("Connection error")
        return None
    except requests.exceptions.Timeout:
        print("Request timed out")
        return None

Webhook Signature Verification

import hmac
import hashlib
import time
 
def verify_webhook_signature(
    body: bytes,
    signature_header: str,
    secret: str,
    tolerance_seconds: int = 300
) -> bool:
    """Verify webhook signature."""
    # Parse signature header: "t=<timestamp>,v1=<signature>"
    parts = dict(p.split('=', 1) for p in signature_header.split(','))
    timestamp = int(parts.get('t', 0))
    received_sig = parts.get('v1', '')
    
    # Check timestamp freshness
    if abs(time.time() - timestamp) > tolerance_seconds:
        return False
    
    # Compute expected signature
    expected_sig = hmac.new(
        secret.encode('utf-8'),
        body,
        hashlib.sha256
    ).hexdigest()
    
    # Timing-safe comparison
    return hmac.compare_digest(expected_sig, received_sig)
 
 
# Flask webhook handler example
from flask import Flask, request, abort
 
app = Flask(__name__)
WEBHOOK_SECRET = os.getenv("LORN_WEBHOOK_SECRET")
 
@app.route("/webhooks/lorn", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Webhook-Signature", "")
    
    if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
        abort(401)
    
    event = request.json
    event_type = event.get("type")
    
    if event_type == "order.created":
        handle_order_created(event["data"]["object"])
    elif event_type == "order.fulfilled":
        handle_order_fulfilled(event["data"]["object"])
    
    return {"received": True}

Async Support

import aiohttp
import asyncio
 
class AsyncLornClient:
    """Async Lorn AI client."""
    
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url
        self.headers = {
            "X-ACP-API-Key": api_key,
            "Content-Type": "application/json"
        }
    
    async def search_products(self, q: str = None, **params) -> dict:
        """Search products asynchronously."""
        if q:
            params["q"] = q
        
        async with aiohttp.ClientSession() as session:
            async with session.get(
                f"{self.base_url}/acp/products",
                headers=self.headers,
                params=params
            ) as response:
                response.raise_for_status()
                return await response.json()
 
 
async def main():
    client = AsyncLornClient(LORN_BASE_URL, LORN_API_KEY)
    results = await client.search_products(q="running shoes")
    print(results)
 
 
if __name__ == "__main__":
    asyncio.run(main())