Documentation

Python SDK

Python 3.10+
pip install

Send emails with Python using the official Laneful Python SDK. Built for modern Python applications with async support and comprehensive features.

Full Feature Support

Templates, attachments, tracking, webhooks

Async Support

Both sync and async operations

Modern Python

Python 3.10+ with type hints

Prerequisites

To get the most out of this guide, you'll need:

  • • Python 3.10 or higher
  • • pip package manager
  • • A Laneful account with API key
  • • Verified domain for sending emails

Installation

pip

Install the Laneful Python SDK using pip:

# Basic installation
pip install laneful

# With async support
pip install laneful[async]

Quick Start

Send your first email in minutes:

Send Simple Text Email

from laneful import LanefulClient, Email, Address

# Initialize the client
client = LanefulClient(
    base_url="https://your-endpoint.send.laneful.net",
    auth_token="your-auth-token"
)

# Create an email
email = Email(
    from_address=Address(email="sender@example.com", name="Your Name"),
    to=[Address(email="recipient@example.com", name="Recipient Name")],
    subject="Hello from Laneful!",
    text_content="This is a simple test email sent using the Laneful Python SDK.",
    html_content="<h1>Hello from Laneful!</h1><p>This is a simple test email sent using the Laneful Python SDK.</p>"
)

# Send the email
try:
    response = client.send_email(email)
    print("✓ Email sent successfully!")
    print(f"Response status: {response.status}")
except Exception as e:
    print(f"✗ Error: {e}")

Examples

Common use cases and patterns:

HTML Email with Tracking

from laneful import LanefulClient, Email, Address, TrackingSettings

email = Email(
    from_address=Address(email="sender@example.com", name="Your Name"),
    to=[Address(email="recipient@example.com", name="Recipient Name")],
    subject="HTML Email with Tracking",
    html_content="<h1>Welcome!</h1><p>This is an <strong>HTML email</strong> with tracking enabled.</p>",
    text_content="Welcome! This is an HTML email with tracking enabled.",
    tag="welcome-email",
    tracking=TrackingSettings(
        opens=True,
        clicks=True,
        unsubscribes=True
    )
)

response = client.send_email(email)

Template Email

email = Email(
    from_address=Address(email="sender@example.com", name="Your Name"),
    to=[Address(email="user@example.com", name="User")],
    template_id="welcome-template",
    template_data={
        "name": "John Doe",
        "company": "Acme Corporation",
        "activation_link": "https://example.com/activate"
    }
)

response = client.send_email(email)

Email with Attachments

import base64
from laneful import LanefulClient, Email, Address, Attachment

# Create attachment from file
with open("/path/to/document.pdf", "rb") as f:
    attachment_data = base64.b64encode(f.read()).decode()

email = Email(
    from_address=Address(email="sender@example.com", name="Your Name"),
    to=[Address(email="user@example.com", name="User")],
    subject="Document Attached",
    text_content="Please find the document attached.",
    attachments=[
        Attachment(
            file_name="document.pdf",
            content=attachment_data,
            content_type="application/pdf"
        )
    ]
)

response = client.send_email(email)

Multiple Recipients with Reply-To

email = Email(
    from_address=Address(email="sender@example.com", name="Your Name"),
    to=[
        Address(email="user1@example.com", name="User One"),
        Address(email="user2@example.com", name="User Two")
    ],
    cc=[Address(email="cc@example.com", name="CC Recipient")],
    bcc=[Address(email="bcc@example.com", name="BCC Recipient")],
    reply_to=Address(email="reply@example.com", name="Reply To"),
    subject="Email to Multiple Recipients",
    text_content="This email is being sent to multiple recipients."
)

response = client.send_email(email)

Scheduled Email

import time

# Schedule for 24 hours from now
send_time = int(time.time()) + (24 * 60 * 60)

email = Email(
    from_address=Address(email="sender@example.com", name="Your Name"),
    to=[Address(email="user@example.com", name="User")],
    subject="Scheduled Email",
    text_content="This email was scheduled.",
    send_time=send_time
)

response = client.send_email(email)

Async Email Sending

import asyncio
from laneful import AsyncLanefulClient, Email, Address

async def send_async_email():
    async with AsyncLanefulClient(
        base_url="https://your-endpoint.send.laneful.net",
        auth_token="your-auth-token"
    ) as client:
        email = Email(
            from_address=Address(email="sender@example.com", name="Your Name"),
            to=[Address(email="user@example.com", name="User")],
            subject="Async Email",
            text_content="This email was sent asynchronously.",
            html_content="<h1>Async Email</h1><p>This email was sent asynchronously.</p>"
        )
        
        # Send email
        try:
            response = await client.send_email(email)
            print("✓ Email sent successfully!")
            print(f"Response status: {response.status}")
            return response
        except Exception as e:
            print(f"✗ Error: {e}")
            raise

# Run async function
asyncio.run(send_async_email())

Batch Email Sending

emails = [
    Email(
        from_address=Address(email="sender@example.com", name="Your Name"),
        to=[Address(email="user1@example.com", name="User One")],
        subject="Email 1",
        text_content="First email content."
    ),
    Email(
        from_address=Address(email="sender@example.com", name="Your Name"),
        to=[Address(email="user2@example.com", name="User Two")],
        subject="Email 2",
        text_content="Second email content."
    )
]

response = client.send_emails(emails)

Context Manager Usage

# Sync context manager - automatic resource cleanup
with LanefulClient(
    base_url="https://your-endpoint.send.laneful.net",
    auth_token="your-auth-token"
) as client:
    email = Email(
        from_address=Address(email="sender@example.com", name="Your Name"),
        to=[Address(email="user@example.com", name="User")],
        subject="Context Manager Email",
        text_content="This email was sent using a context manager."
    )
    response = client.send_email(email)
    print(f"Email sent: {response.status}")
# Client session automatically closed

# Async context manager
async with AsyncLanefulClient(
    base_url="https://your-endpoint.send.laneful.net",
    auth_token="your-auth-token"
) as client:
    email = Email(
        from_address=Address(email="sender@example.com", name="Your Name"),
        to=[Address(email="user@example.com", name="User")],
        subject="Async Context Manager Email",
        text_content="This email was sent using an async context manager."
    )
    response = await client.send_email(email)
    print(f"Email sent: {response.status}")
# Client session automatically closed

Webhook Handler Example

from flask import Flask, request, jsonify
from laneful.webhooks import WebhookHandler, WebhookEvent
import json

app = Flask(__name__)

# Initialize webhook handler
webhook_handler = WebhookHandler(webhook_secret="your-webhook-secret")

# Register event handlers
@webhook_handler.on("email.delivered")
def handle_delivered(event: WebhookEvent):
    print(f"Email {event.message_id} delivered to {event.email}")

@webhook_handler.on("email.opened")
def handle_opened(event: WebhookEvent):
    print(f"Email {event.message_id} opened by {event.email}")

@webhook_handler.on("email.clicked")
def handle_clicked(event: WebhookEvent):
    url = event.data.get("url")
    print(f"Link clicked by {event.email}: {url}")

@webhook_handler.on("email.bounced")
def handle_bounced(event: WebhookEvent):
    is_hard = event.data.get("is_hard")
    print(f"Email bounced ({'hard' if is_hard else 'soft'}) for: {event.email}")

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    try:
        # Get raw payload
        payload = request.get_data(as_text=True)
        
        # Extract signature from headers
        signature = request.headers.get('x-webhook-signature')
        if not signature:
            return jsonify({'error': 'Missing signature'}), 401
        
        # Verify signature and process webhook
        if webhook_handler.verify_signature(payload, signature):
            webhook_handler.process_webhook(payload)
            return jsonify({'status': 'success'})
        else:
            return jsonify({'error': 'Invalid signature'}), 401
        
    except Exception as e:
        return jsonify({'error': str(e)}), 400

if __name__ == '__main__':
    app.run(debug=True)

API Reference

LanefulClient

Constructors

  • LanefulClient(base_url: str, auth_token: str) - Initialize client with endpoint and auth token

Methods

  • send_email(email: Email) → Response - Send single email
  • send_emails(emails: List[Email]) → Response - Send multiple emails

Email

Required Fields

  • from_address: Address - Sender address

Optional Fields

  • to: List[Address] - Recipient addresses
  • cc: List[Address] - CC recipient addresses
  • bcc: List[Address] - BCC recipient addresses
  • reply_to: Address - Reply-to address
  • subject: str - Email subject
  • text_content: str - Plain text content
  • html_content: str - HTML content
  • template_id: str - Template ID
  • template_data: dict - Template data
  • attachments: List[dict] - File attachments
  • send_time: int - Scheduled send time (Unix timestamp)
  • tag: str - Email tag for categorization

Utility Classes

  • Address(email: str, name: str = None) - Email address with optional name
  • Attachment(file_name: str, content: str, content_type: str) - Email attachment with base64 encoded content
  • TrackingSettings(opens: bool, clicks: bool, unsubscribes: bool) - Email tracking configuration
  • AsyncLanefulClient - Async version of the client for async/await operations

WebhookHandler

Initialization

  • WebhookHandler(webhook_secret: str = None) - Initialize webhook handler with optional secret

Signature Verification

  • verify_signature(payload: str, signature: str) → bool - Verifies webhook signature (supports sha256= prefix)

Event Handling

  • on(event_type: str) - Decorator to register event handlers
  • register_handler(event_type: str, handler: Callable) - Register event handler function
  • process_webhook(payload: str) - Process webhook payload and call appropriate handlers

WebhookEvent

  • event_type: str - Type of webhook event
  • message_id: str - Message ID of the email
  • email: str - Email address of the recipient
  • timestamp: int - Unix timestamp of the event
  • data: Dict[str, Any] - Additional event data

Error Handling

Comprehensive error handling with specific exception types:

from laneful.exceptions import LanefulError, LanefulAPIError, LanefulAuthError

try:
    response = client.send_email(email)
    print("✓ Email sent successfully!")
    print(f"Response status: {response.status}")
except LanefulAuthError:
    print("✗ Authentication failed - check your API token")
except LanefulAPIError as e:
    print(f"✗ API error: {e.message} (status: {e.status_code})")
except LanefulError as e:
    print(f"✗ Client error: {e.message}")
except Exception as e:
    print(f"✗ Unexpected error: {e}")

Error Handling

  • LanefulAuthError - Authentication failures
  • LanefulAPIError - API request failures
  • LanefulError - General client errors
  • Exception - Unexpected errors

Best Practices

  • • Always wrap API calls in try-except
  • • Handle specific exception types first
  • • Log errors with context information
  • • Implement retry logic for transient failures

Webhook Handling

Comprehensive webhook handling with signature verification, payload parsing, and validation:

Basic Signature Verification

from laneful.webhooks import WebhookHandler

# Initialize webhook handler
webhook_handler = WebhookHandler(webhook_secret="your-webhook-secret")

# In your webhook handler
payload = request.get_data(as_text=True)  # Get the raw request body
signature = request.headers.get('x-webhook-signature')

if webhook_handler.verify_signature(payload, signature):
    # Process webhook data
    webhook_handler.process_webhook(payload)
    # Handle webhook events
else:
    # Invalid signature
    return jsonify({'error': 'Invalid signature'}), 401

Advanced Webhook Processing

from laneful.webhooks import WebhookHandler, WebhookEvent

# Initialize webhook handler with event handlers
webhook_handler = WebhookHandler(webhook_secret="your-webhook-secret")

# Register event handlers
@webhook_handler.on("email.delivered")
def handle_delivery_event(event: WebhookEvent):
    print(f"Email {event.message_id} delivered to {event.email}")

@webhook_handler.on("email.opened")
def handle_open_event(event: WebhookEvent):
    print(f"Email {event.message_id} opened by {event.email}")

@webhook_handler.on("email.clicked")
def handle_click_event(event: WebhookEvent):
    url = event.data.get("url")
    print(f"Link clicked by {event.email}: {url}")

@webhook_handler.on("email.bounced")
def handle_bounce_event(event: WebhookEvent):
    is_hard = event.data.get("is_hard")
    print(f"Email bounced ({'hard' if is_hard else 'soft'}) for: {event.email}")

@webhook_handler.on("email.complained")
def handle_spam_complaint_event(event: WebhookEvent):
    print(f"Spam complaint from {event.email}")

@webhook_handler.on("email.unsubscribed")
def handle_unsubscribe_event(event: WebhookEvent):
    print(f"Unsubscribe from {event.email}")

# Complete webhook verification and processing workflow
try:
    # Step 1: Get raw payload
    payload = request.get_data(as_text=True)
    
    # Step 2: Extract signature from headers
    signature = request.headers.get('x-webhook-signature')
    if not signature:
        raise ValueError("Missing webhook signature")
    
    # Step 3: Verify signature and process webhook
    if webhook_handler.verify_signature(payload, signature):
        webhook_handler.process_webhook(payload)
        return jsonify({'status': 'success'})
    else:
        return jsonify({'error': 'Invalid signature'}), 401
    
except ValueError as e:
    # Payload validation error
    return jsonify({'error': str(e)}), 400
except Exception as e:
    # Other errors
    return jsonify({'error': str(e)}), 401

Batch Mode Support

# The WebhookHandler automatically processes both single events and batches
# No need to check for batch mode - the handler manages this internally

# Single event payload
single_payload = '{"event_type": "email.delivered", "message_id": "123", ...}'
webhook_handler.process_webhook(single_payload)

# Batch event payload (multiple events in one webhook)
batch_payload = '{"events": [{"event_type": "email.delivered", ...}, {"event_type": "email.opened", ...}]}'
webhook_handler.process_webhook(batch_payload)

# All registered event handlers will be called for each event automatically

Supported Webhook Events

Delivery Events

  • email.delivered - Email delivered successfully
  • email.bounced - Email bounced (hard or soft)
  • email.failed - Email failed to send
  • email.complained - Recipient marked email as spam

Engagement Events

  • email.opened - Email opened by recipient
  • email.clicked - Link clicked in email
  • email.unsubscribed - Recipient unsubscribed

Webhook Features

Security

  • • HMAC-SHA256 signature verification
  • • Support for sha256= prefix
  • • Constant-time comparison (timing attack protection)
  • • Multiple header format support

Validation

  • • JSON payload structure validation
  • • Required field validation
  • • Event type validation
  • • Email format validation
  • • UUID format validation for lane_id

Ready to Get Started?

Start sending emails with Python in minutes. Check out the examples and integrate with your application.