mailblock 1.1.0
Official Python SDK for MailBlock email service
Requires Python: >=3.8

# MailBlock Python SDK

![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)
![Version](https://img.shields.io/badge/version-1.0.0-green)
![License](https://img.shields.io/badge/license-MIT-blue)

Official Python SDK for the MailBlock email service. Send emails with confidence using a clean, Pythonic interface with comprehensive error handling, logging, and validation.

## 🚀 Features

- **Fluent Builder Pattern** - Intuitive, chainable API for email construction
- **Comprehensive Error Handling** - Detailed error messages with helpful suggestions
- **Async/Await Support** - Both synchronous and asynchronous email sending
- **Email Scheduling** - Schedule emails for future delivery
- **Robust Validation** - Client-side validation with detailed feedback
- **Advanced Logging** - Built-in debugging and request tracking
- **Type Hints** - Full type safety with mypy support
- **Context Manager** - Proper resource management
- **Retry Mechanism** - Automatic retry with exponential backoff

# MailBlock Python SDK

![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)
![Version](https://img.shields.io/badge/version-1.0.0-green)
![License](https://img.shields.io/badge/license-MIT-blue)

Official Python SDK for the MailBlock email service. Send emails with confidence using a clean, Pythonic interface with comprehensive error handling, logging, and validation.

## 🚀 Features

- **Fluent Builder Pattern** - Intuitive, chainable API for email construction
- **Comprehensive Error Handling** - Detailed error messages with helpful suggestions
- **Async/Await Support** - Both synchronous and asynchronous email sending
- **Email Scheduling** - Schedule emails for future delivery
- **Robust Validation** - Client-side validation with detailed feedback
- **Advanced Logging** - Built-in debugging and request tracking
- **Type Hints** - Full type safety with mypy support
- **Context Manager** - Proper resource management
- **Retry Mechanism** - Automatic retry with exponential backoff

## 📦 Installation

```bash
# Install from PyPI
pip install mailblock

# For async support
pip install mailblock[async]

# For development
pip install mailblock[dev]
```

## 🔧 Quick Start

```python
from mailblock import MailBlock

# Initialize client
client = MailBlock("your-api-key")

# Send a simple email
response = client.email() \
    .to("recipient@example.com") \
    .from_email("sender@example.com") \
    .subject("Hello from MailBlock!") \
    .text("This is a test email from the MailBlock Python SDK.") \
    .send_sync()

if response.success:
    print(f"Email sent! ID: {response.data['id']}")
else:
    print(f"Error: {response.error}")
```

## 📚 Documentation

### Client Initialization

```python
from mailblock import MailBlock

# Basic initialization
client = MailBlock("your-api-key")

# With custom configuration
client = MailBlock(
    api_key="your-api-key",
    base_url="https://api.mailblock.com",  # Custom API endpoint
    timeout=30,                            # Request timeout in seconds
    max_retries=3,                        # Maximum retry attempts
    retry_delay=1.0,                      # Base retry delay in seconds
    debug=True                            # Enable debug logging
)
```

### Basic Email Sending

#### Synchronous

```python
# Simple text email
response = client.email() \
    .to("recipient@example.com") \
    .from_email("sender@example.com") \
    .subject("Hello World") \
    .text("This is a plain text email.") \
    .send_sync()

# HTML email with fallback text
response = client.email() \
    .to("user@example.com") \
    .from_email("noreply@yourapp.com") \
    .subject("Welcome!") \
    .text("Welcome to our service!") \
    .html("<h1>Welcome!</h1><p>Thanks for joining us.</p>") \
    .send_sync()
```

#### Asynchronous

```python
import asyncio

async def send_email():
    response = await client.email() \
        .to("recipient@example.com") \
        .from_email("sender@example.com") \
        .subject("Async Email") \
        .text("This email was sent asynchronously!") \
        .send()
    return response

# Run async function
response = asyncio.run(send_email())
```

### Email Scheduling

```python
from datetime import datetime, timedelta

# Schedule for 1 hour from now
send_time = datetime.now() + timedelta(hours=1)

response = client.email() \
    .to("recipient@example.com") \
    .from_email("scheduler@example.com") \
    .subject("Scheduled Email") \
    .text("This email was scheduled for delivery.") \
    .schedule_at(send_time) \
    .send_sync()

# Schedule with date string
response = client.email() \
    .to("recipient@example.com") \
    .from_email("scheduler@example.com") \
    .subject("Scheduled Email") \
    .text("Scheduled via date string.") \
    .schedule_at("2024-12-25T10:00:00") \
    .send_sync()
```

### Error Handling

```python
from mailblock import ValidationError, AuthenticationError, RateLimitError

try:
    response = client.email() \
        .to("invalid-email") \
        .from_email("sender@example.com") \
        .subject("Test") \
        .text("Test content") \
        .send_sync()

except ValidationError as e:
    print(f"Validation error: {e}")
    print(f"Suggestion: {e.suggestion}")

except AuthenticationError as e:
    print(f"Authentication failed: {e}")
    print(f"Request ID: {e.request_id}")

except RateLimitError as e:
    print(f"Rate limited: {e}")
    print(f"Suggestion: {e.suggestion}")

except Exception as e:
    print(f"Unexpected error: {e}")
```

### Advanced Usage

#### Bulk Email Sending

```python
import asyncio

async def send_bulk_emails():
    recipients = [
        "user1@example.com",
        "user2@example.com",
        "user3@example.com"
    ]

    # Send emails concurrently
    tasks = []
    for recipient in recipients:
        task = client.email() \
            .to(recipient) \
            .from_email("bulk@example.com") \
            .subject("Bulk Email") \
            .text(f"Personal email for {recipient}") \
            .send()
        tasks.append(task)

    responses = await asyncio.gather(*tasks, return_exceptions=True)

    for i, response in enumerate(responses):
        if isinstance(response, Exception):
            print(f"Email {i+1} failed: {response}")
        elif response.success:
            print(f"Email {i+1} sent successfully")
        else:
            print(f"Email {i+1} failed: {response.error}")

asyncio.run(send_bulk_emails())
```

#### Custom Logger

```python
import logging

# Set up custom logger
logger = logging.getLogger("my_app.mailblock")
logger.setLevel(logging.DEBUG)

handler = logging.FileHandler("email.log")
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

# Use custom logger
client = MailBlock("your-api-key", debug=True, logger=logger)
```

#### Context Manager

```python
# Ensures proper cleanup of resources
with MailBlock("your-api-key") as client:
    response = client.email() \
        .to("recipient@example.com") \
        .from_email("sender@example.com") \
        .subject("Context Manager Test") \
        .text("This uses proper resource management.") \
        .send_sync()
```

#### Direct Email Data

```python
from mailblock import EmailData

# Create email data directly
email_data = EmailData(
    to="recipient@example.com",
    from_email="sender@example.com",
    subject="Direct Send",
    text="Sent without builder pattern",
    html="<p>Sent <strong>without</strong> builder pattern</p>"
)

# Send directly
response = client.send_email_sync(email_data)
```

## 🔍 API Response

All send methods return an `APIResponse` object with the following structure:

```python
class APIResponse:
    success: bool                    # Whether the request succeeded
    request_id: str                 # Unique request identifier
    timestamp: datetime             # When the request was made
    duration: int                   # Request duration in milliseconds

    # On success
    data: Dict[str, Any]           # Response data (includes email ID)
    message: str                   # Success message

    # On error
    error: str                     # Error message
    error_type: str               # Error category
    suggestion: str               # Helpful suggestion
    status_code: int              # HTTP status code
    endpoint: str                 # API endpoint used
```

### Success Response Example

```python
if response.success:
    print(f"Email ID: {response.data['id']}")
    print(f"Status: {response.data['status']}")
    print(f"Duration: {response.duration}ms")
    print(f"Request ID: {response.request_id}")
```

### Error Response Example

```python
if not response.success:
    print(f"Error: {response.error}")
    print(f"Type: {response.error_type}")
    print(f"Suggestion: {response.suggestion}")
    print(f"Status Code: {response.status_code}")
    print(f"Request ID: {response.request_id}")
```

## 🛡️ Exception Hierarchy

```python
MailBlockError                    # Base exception
├── ValidationError              # Client-side validation errors
├── AuthenticationError          # Invalid API key (401)
├── AuthorizationError           # Insufficient permissions (403)
├── RateLimitError              # Rate limiting (429)
├── ServerError                 # Server errors (5xx)
├── NetworkError                # Connection issues
└── TimeoutError                # Request timeouts
```

## ⚙️ Configuration Options

| Parameter     | Type     | Default                                              | Description                 |
| ------------- | -------- | ---------------------------------------------------- | --------------------------- |
| `api_key`     | `str`    | Required                                             | Your MailBlock API key      |
| `base_url`    | `str`    | `https://sdk-backend-production-20e1.up.railway.app` | API base URL                |
| `timeout`     | `int`    | `30`                                                 | Request timeout in seconds  |
| `max_retries` | `int`    | `3`                                                  | Maximum retry attempts      |
| `retry_delay` | `float`  | `1.0`                                                | Base retry delay in seconds |
| `debug`       | `bool`   | `False`                                              | Enable debug logging        |
| `logger`      | `Logger` | `None`                                               | Custom logger instance      |

## 🧪 Testing

```bash
# Install development dependencies
pip install -e .[dev]

# Run tests
pytest

# Run tests with coverage
pytest --cov=mailblock

# Run specific test file
pytest tests/test_client.py

# Run with verbose output
pytest -v
```

## 🏗️ Development

```bash
# Clone repository
git clone https://github.com/5ysc4ll/mailblock-python.git
cd mailblock-python

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install in development mode
pip install -e .[dev]

# Run linting
flake8 mailblock tests
black mailblock tests
isort mailblock tests

# Type checking
mypy mailblock
```

## 📋 Examples

Check out the `examples/` directory for comprehensive usage examples:

- [`basic_usage.py`](examples/basic_usage.py) - Basic email sending patterns
- [`advanced_usage.py`](examples/advanced_usage.py) - Advanced features and async usage

## 🤝 Contributing

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Add tests for new functionality
5. Run the test suite (`pytest`)
6. Commit your changes (`git commit -m 'Add amazing feature'`)
7. Push to the branch (`git push origin feature/amazing-feature`)
8. Open a Pull Request

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🐛 Issues & Support

- **Bug Reports**: [GitHub Issues](https://github.com/5ysc4ll/python-sdk/issues)
- **Feature Requests**: [GitHub Issues](https://github.com/5ysc4ll/python-sdk/issues)
- **Documentation**: [MailBlock Docs](https://docs.mailblock.com)
- **Support**: [support@mailblock.com](mailto:support@mailblock.com)

## 🔗 Links

- **GitHub**: [https://github.com/5ysc4ll/python-sdk](https://github.com/5ysc4ll/python-sdk)
- **PyPI**: [https://pypi.org/project/mailblock/](https://pypi.org/project/mailblock/)
- **Documentation**: [https://docs.mailblock.com](https://docs.mailblock.com)
- **MailBlock Website**: [https://mailblock.com](https://mailblock.com)

## 📊 Changelog

### v1.0.0 (Initial Release)

- ✨ Fluent builder pattern for email construction
- 🔄 Both sync and async email sending
- ⏰ Email scheduling support
- 🛡️ Comprehensive error handling and validation
- 📝 Advanced logging and debugging
- 🔁 Automatic retry with exponential backoff
- 📚 Complete type hints and mypy support
- ✅ Comprehensive test suite
- 📖 Full documentation and examples

## 🆕 Enhanced Features (v1.1.0)

### Multiple Recipients & CC/BCC

Send emails to multiple recipients with carbon copy and blind carbon copy support:

```python
# Multiple recipients with CC and BCC
response = client.email() \
    .to(["user1@example.com", "user2@example.com"]) \
    .cc("manager@example.com") \
    .bcc(["archive@example.com", "backup@example.com"]) \
    .from_email("sender@example.com") \
    .subject("Team Update") \
    .text("This email goes to multiple recipients") \
    .send_sync()
```

### Email Cancellation

Cancel scheduled emails before they are sent:

```python
# Cancel a single email
response = client.cancel_email_sync("email_id_123")
if response.success:
    print(f"Email cancelled: {response.data['current_status']}")

# Cancel multiple emails in bulk
email_ids = ["email_1", "email_2", "email_3"]
response = client.cancel_emails_sync(email_ids)
print(f"Cancelled {response.data['success_count']} emails")
```

### Update Scheduled Emails

Modify scheduled emails before they are sent:

```python
from mailblock import UpdateEmailData
from datetime import datetime, timedelta

# Create update data
updates = UpdateEmailData(
    subject="Updated Subject",
    body_text="Updated text content",
    body_html="<h1>Updated HTML</h1>",
    scheduled_at=datetime.now() + timedelta(hours=2)  # Reschedule
)

# Update the email
response = client.update_scheduled_email_sync("email_id_123", updates)
if response.success:
    print("Email updated successfully")
```

### Async Support

All new methods support async operations:

```python
import asyncio

async def manage_emails():
    # Async email cancellation
    response = await client.cancel_email("email_id_123")
    
    # Async bulk cancellation
    response = await client.cancel_emails(["email_1", "email_2"])
    
    # Async email updates
    updates = UpdateEmailData(subject="New Subject")
    response = await client.update_scheduled_email("email_id_123", updates)

asyncio.run(manage_emails())
```

### Enhanced Validation

Comprehensive validation for all email operations:

```python
# Validates email formats in arrays
try:
    client.email().to(["valid@example.com", "invalid-email"])
except ValidationError as e:
    print(f"Validation failed: {e}")

# Validates email IDs
try:
    client.cancel_emails_sync([])  # Empty array
except ValidationError as e:
    print(f"Validation failed: {e}")
```

---

Built with ❤️ by the MailBlock Team