# Python

## 1. Installation

Install the SDK via pip:

<CodeTabs>
```bash title="pip"
pip install lettermint
```
```bash title="poetry"
poetry add lettermint
```
```bash title="uv"
uv add lettermint
```
</CodeTabs>

:::note
Requires Python 3.9 or higher.
:::

## 2. Send your first email

Initialize the client with your API token:

```python
import os
from lettermint import Lettermint

client = Lettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN"))
```

Send your first email:

```python
response = (
    client.email
    .from_("John Doe <john@yourdomain.com>")
    .to("recipient@example.com")
    .subject("Hello from Lettermint")
    .text("This is a test email sent using the Lettermint Python SDK.")
    .send()
)

print(f"Email sent with ID: {response['message_id']}")
```

## 3. Email Features

### Basic Email

Send a simple text or HTML email:

```python
response = (
    client.email
    .from_("John Doe <john@yourdomain.com>")
    .to("recipient@example.com")
    .subject("Your account is ready!")
    .html("<h1>Welcome!</h1><p>Thanks for signing up.</p>")
    .text("Welcome! Thanks for signing up.")
    .send()
)
```

### Multiple Recipients

Send to multiple recipients using CC and BCC:

```python
response = (
    client.email
    .from_("John Doe <john@yourdomain.com>")
    .to("user1@example.com", "user2@example.com")
    .cc("manager@yourdomain.com")
    .bcc("archive@yourdomain.com")
    .subject("Monthly Newsletter")
    .html("<h1>This Month's Updates</h1>")
    .send()
)
```

### Custom Headers and Reply-To

Add custom headers and set reply-to addresses:

```python
response = (
    client.email
    .from_("support@yourdomain.com")
    .to("customer@example.com")
    .reply_to("help@yourdomain.com")
    .subject("Support Ticket #12345")
    .headers({
        "X-Priority": "1",
        "X-Ticket-ID": "12345"
    })
    .html("<p>Your support ticket has been updated.</p>")
    .send()
)
```

### Metadata

Add metadata for tracking and webhook payloads:

```python
response = (
    client.email
    .from_("notifications@yourdomain.com")
    .to("user@example.com")
    .subject("Order Confirmation")
    .metadata({
        "order_id": "12345",
        "customer_id": "cust_789",
        "campaign": "order_confirmation"
    })
    .html("<p>Your order has been confirmed.</p>")
    .send()
)
```

:::note
Metadata is included in webhook payloads but not added to the actual email headers. Use it for tracking and analytics purposes.
:::

### Tags

Categorize emails for filtering and analytics:

```python
response = (
    client.email
    .from_("alerts@yourdomain.com")
    .to("admin@example.com")
    .subject("System Alert")
    .tag("system-alerts")
    .html("<p>Critical system alert detected.</p>")
    .send()
)
```

:::note
One tag per message. Tags can contain letters, numbers, hyphens, underscores, and spaces (max 255 characters).
See [Tags documentation](/platform/emails/tags) for more details.
:::

### Route Selection

Direct emails to specific routes within your project:

```python
response = (
    client.email
    .from_("notifications@yourdomain.com")
    .to("user@example.com")
    .subject("Welcome!")
    .route("transactional")
    .html("<p>Welcome to our platform.</p>")
    .send()
)
```

### File Attachments

Attach files to your emails:

```python
import base64

# Read and encode file
with open("/path/to/document.pdf", "rb") as f:
    encoded_content = base64.b64encode(f.read()).decode()

response = (
    client.email
    .from_("invoices@yourdomain.com")
    .to("customer@example.com")
    .subject("Your Invoice")
    .html("<p>Please find your invoice attached.</p>")
    .attach("invoice.pdf", encoded_content)
    .send()
)
```

### Inline Images

Embed images directly in your HTML using Content-ID:

```python
import base64

with open("/path/to/logo.png", "rb") as f:
    encoded_logo = base64.b64encode(f.read()).decode()

response = (
    client.email
    .from_("marketing@yourdomain.com")
    .to("customer@example.com")
    .subject("Welcome to Our Platform")
    .html('<p>Welcome!</p><img src="cid:logo@yourdomain.com" alt="Logo">')
    .attach("logo.png", encoded_logo, "logo@yourdomain.com")
    .send()
)
```

### Idempotency

Prevent duplicate emails with idempotency keys:

```python
response = (
    client.email
    .from_("notifications@yourdomain.com")
    .to("user@example.com")
    .subject("Order Confirmation")
    .html("<p>Your order has been confirmed.</p>")
    .idempotency_key("order-12345-confirmation")
    .send()
)
```

:::tip
Use a unique key per logical email (e.g., combining order ID + email type). Retrying with the same key won't send duplicate emails.
:::

## 4. Async Support

The SDK provides an async client for use with `asyncio`:

```python
import asyncio
import os
from lettermint import AsyncLettermint

async def send_email():
    async with AsyncLettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN")) as client:
        response = await (
            client.email
            .from_("John Doe <john@yourdomain.com>")
            .to("recipient@example.com")
            .subject("Hello from Lettermint")
            .text("This is a test email.")
            .send()
        )
        print(f"Email sent with ID: {response['message_id']}")

asyncio.run(send_email())
```

:::tip
Use the async client in FastAPI, Starlette, or other async frameworks for better performance.
:::

## 5. Client Configuration

Customize the client with optional parameters:

```python
client = Lettermint(
    api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN"),
    base_url="https://api.lettermint.co/v1",  # Custom API URL
    timeout=60.0,  # Request timeout in seconds
)
```

Use the client as a context manager for automatic resource cleanup:

```python
with Lettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN")) as client:
    response = (
        client.email
        .from_("sender@yourdomain.com")
        .to("recipient@example.com")
        .subject("Test")
        .text("Hello!")
        .send()
    )
```

## 6. Response

```python
response = (
    client.email
    .from_("John Doe <john@yourdomain.com>")
    .to("recipient@example.com")
    .subject("Test")
    .text("Hello!")
    .send()
)

print(response["message_id"])  # Unique email ID
```

## 7. Error Handling

Handle errors with specific exception types:

```python
from lettermint import Lettermint
from lettermint.exceptions import (
    ValidationError,
    ClientError,
    TimeoutError,
    HttpRequestError,
)

client = Lettermint(api_token=os.environ.get("LETTERMINT_PROJECT_TOKEN"))

try:
    response = (
        client.email
        .from_("sender@yourdomain.com")
        .to("recipient@example.com")
        .subject("Test")
        .text("Hello!")
        .send()
    )
except ValidationError as e:
    # 422 errors (invalid parameters, daily limit exceeded, etc.)
    print(f"Validation error: {e.error_type}")
except ClientError as e:
    # 400 errors (bad request)
    print(f"Client error: {e}")
except TimeoutError as e:
    # Request timed out
    print(f"Timeout: {e}")
except HttpRequestError as e:
    # Other HTTP errors
    print(f"HTTP error {e.status_code}: {e}")
```

## Next Steps

<CardGroup cols={2}>
    <Card title="Tags" icon="tag" href="/platform/emails/tags">
        Organize and filter emails with tags.
    </Card>
    <Card title="Tracking" icon="chart-line" href="/platform/emails/tracking/introduction">
        Track opens, clicks, and deliverability.
    </Card>
    <Card title="Webhooks" icon="webhook" href="/platform/webhooks/introduction">
        Receive real-time delivery notifications.
    </Card>
    <Card title="SMTP Alternative" icon="envelope" href="/guides/send-email-with-smtp">
        Send via SMTP instead of the API.
    </Card>
</CardGroup>

<Card title="GitHub Repository" icon="github" href="https://github.com/lettermint/lettermint-python">
    Find the complete source code, report issues, or contribute on GitHub.
</Card>
