# ZeptoMail

[![PyPI version](https://img.shields.io/pypi/v/zeptomail.svg)](https://pypi.org/project/zeptomail/)
[![Python versions](https://img.shields.io/pypi/pyversions/zeptomail.svg)](https://pypi.org/project/zeptomail/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

A lightweight Python client for the [ZeptoMail](https://www.zoho.com/zeptomail/) transactional email API.

## Features

- Simple, Pythonic API for sending transactional emails
- Support for HTML and plain-text email bodies
- File attachments with MIME type validation
- Reply-to and bounce address support
- Proper error handling with custom exceptions
- Fully typed (PEP 561 compatible)
- Zero configuration — just add your API key

## Installation

```bash
pip install zeptomail
```

## Quick Start

```python
from zeptomail import Config, Email

config = Config(api_key="your-zeptomail-api-key")
email = Email(config, bounce_address="bounce@yourdomain.com")

response = email.send(
    from_="noreply@yourdomain.com",
    from_name="My Application",
    to=["user@example.com"],
    subject="Welcome!",
    html_body="<h1>Hello!</h1><p>Welcome to our platform.</p>",
)

print(response)
```

## Usage

### Configuration

```python
from zeptomail import Config

# Basic configuration
config = Config(api_key="your-api-key")

# With custom timeout
config = Config(api_key="your-api-key", timeout=60)
```

### Sending Emails

```python
from zeptomail import Config, Email

config = Config(api_key="your-api-key")
email = Email(config)

# Simple text email
response = email.send(
    from_="noreply@yourdomain.com",
    from_name="Sender Name",
    to=["recipient@example.com"],
    subject="Hello!",
    text_body="This is a plain text email.",
)

# HTML email to multiple recipients
response = email.send(
    from_="noreply@yourdomain.com",
    from_name="Sender Name",
    to=["alice@example.com", "bob@example.com"],
    subject="Newsletter",
    html_body="<h1>Monthly Update</h1><p>Here's what's new...</p>",
)

# Using recipient dicts for custom display names
response = email.send(
    from_="noreply@yourdomain.com",
    from_name="Sender Name",
    to=[
        {"address": "alice@example.com", "name": "Alice"},
        {"address": "bob@example.com", "name": "Bob"},
    ],
    subject="Hello!",
    html_body="<p>Hi there!</p>",
)
```

### Attachments

```python
from zeptomail import Config, Email, MimeType
from zeptomail.utils import file_to_base64

config = Config(api_key="your-api-key")
email = Email(config)

# Attach a file
content = file_to_base64("/path/to/report.pdf")
mime = MimeType("application", "pdf")

response = email.send(
    from_="noreply@yourdomain.com",
    from_name="Sender Name",
    to=["recipient@example.com"],
    subject="Your Report",
    text_body="Please find your report attached.",
    attachments=[(content, mime, "report.pdf")],
)
```

### MIME Types

```python
from zeptomail import MimeType

# Create from components
pdf = MimeType("application", "pdf")
png = MimeType("image", "png")

# Parse from string
csv = MimeType.from_string("text/csv")

# Validate
pdf.is_valid()   # True
exe = MimeType("application", "exe")
exe.is_valid()   # False — .exe is blocked by ZeptoMail
```

### Reply-To

```python
response = email.send(
    from_="noreply@yourdomain.com",
    from_name="Sender Name",
    to=["recipient@example.com"],
    subject="Support Ticket #1234",
    text_body="We received your request.",
    reply_to=[("support@yourdomain.com", "Support Team")],
)
```

### Error Handling

```python
from zeptomail import Config, Email, ZeptoMailAPIError

config = Config(api_key="your-api-key")
email = Email(config)

try:
    response = email.send(
        from_="noreply@yourdomain.com",
        from_name="Sender",
        to=["user@example.com"],
        subject="Test",
        text_body="Hello!",
    )
except ZeptoMailAPIError as exc:
    print(f"API error {exc.status_code}: {exc.response_body}")
```

## API Reference

### `Config(api_key, *, url=None, timeout=30)`

| Parameter | Type   | Description                                           |
|-----------|--------|-------------------------------------------------------|
| `api_key` | `str`  | Your ZeptoMail API key.                               |
| `url`     | `str`  | Override the default API endpoint (optional).         |
| `timeout` | `int`  | Request timeout in seconds. Default: `30`.            |

### `Email(config, bounce_address=None)`

| Parameter        | Type     | Description                        |
|------------------|----------|------------------------------------|
| `config`         | `Config` | A `Config` instance.               |
| `bounce_address` | `str`    | Envelope bounce address (optional).|

### `Email.send(...)`

| Parameter          | Type                              | Description                                      |
|--------------------|-----------------------------------|--------------------------------------------------|
| `from_`            | `str`                             | Sender email address.                            |
| `from_name`        | `str`                             | Sender display name.                             |
| `to`               | `list[str \| dict]`               | Recipient addresses.                             |
| `subject`          | `str`                             | Email subject line.                              |
| `text_body`        | `str \| None`                     | Plain-text body (optional).                      |
| `html_body`        | `str \| None`                     | HTML body (optional).                            |
| `reply_to`         | `list[tuple[str, str]]`           | Reply-to ``(email, name)`` pairs (optional).     |
| `attachments`      | `list[tuple[str, MimeType, str]]` | ``(base64, mime, filename)`` triples (optional). |
| `client_reference` | `str \| None`                     | Tracking reference (optional).                   |

### `MimeType(type_, subtype)`

| Method / Property | Returns | Description                                |
|-------------------|---------|--------------------------------------------|
| `.value`          | `str`   | MIME string, e.g. `"application/pdf"`.     |
| `.is_valid()`     | `bool`  | `True` if not in the blocked list.         |
| `.validate()`     | `self`  | Returns self or raises `MimeTypeError`.    |
| `.from_string(s)` | `MimeType` | Class method to parse `"type/subtype"`. |

## Requirements

- Python 3.8+
- [requests](https://docs.python-requests.org/)

## License

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

## Contributing

Contributions are welcome! Please open an issue or pull request on [GitHub](https://github.com/yourusername/zeptomail).
