Metadata-Version: 2.4
Name: densend
Version: 0.1.0
Summary: Send email with DenSend.
Author-email: DenSend <info@densend.com>
License: MIT
Project-URL: Homepage, https://densend.com
Project-URL: Documentation, https://densend.com/docs
Project-URL: Repository, https://github.com/Udenis123/MAILING
Project-URL: Issues, https://github.com/Udenis123/MAILING/issues
Keywords: email,smtp,transactional,densend
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Communications :: Email
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# densend

The official Python client for [DenSend](https://densend.com).

```bash
pip install densend
```

Requires Python 3.8 or newer.

## Sending

```python
import densend

densend.api_key = "your DENSEND_API_KEY"

email = densend.Emails.send({
    "from": "You <hello@yourdomain.com>",
    "to": "someone@example.com",
    "subject": "Hello from DenSend",
    "html": "<p>Your order has shipped.</p>",
})
print(email["id"])
```

`from` has to be on a domain you have added and verified.

`densend.api_key` also reads from the `DENSEND_API_KEY` environment
variable, so setting it explicitly is only needed when you want to
override that.

Multiple recipients get one id each, because a bounce belongs to exactly one
address:

```python
email = densend.Emails.send({
    "from": "You <hello@yourdomain.com>",
    "to": ["first@example.com", "second@example.com"],
    "cc": ["copied@example.com"],
    "reply_to": "support@yourdomain.com",
    "subject": "Your receipt",
    "text": "Thanks for your order.",
})
print(email["ids"])
```

## Scheduling

Pass a `datetime` or an ISO 8601 string. Anything in the past sends
immediately.

```python
from datetime import datetime, timedelta, timezone

densend.Emails.send({
    "from": "You <hello@yourdomain.com>",
    "to": "someone@example.com",
    "subject": "A reminder",
    "text": "This was scheduled.",
    "scheduled_at": datetime.now(timezone.utc) + timedelta(hours=1),
})
```

## Attachments

```python
with open("invoice.pdf", "rb") as f:
    content = f.read()

densend.Emails.send({
    "from": "You <hello@yourdomain.com>",
    "to": "someone@example.com",
    "subject": "Your invoice",
    "text": "Attached.",
    "attachments": [
        {"filename": "invoice.pdf", "content": content, "content_type": "application/pdf"},
    ],
})
```

`content` can be `bytes` (encoded automatically) or a `str` you have
already base64-encoded yourself. 7MB total across all attachments,
combined. Going over raises a `DenSendError` naming the actual size, not a
generic failure.

## Reading

```python
email = densend.Emails.get(email_id)
print(email["status"], email["opens"], email["clicks"])

recent = densend.Emails.list(limit=20)
```

## Errors

Every non-2xx response raises a `DenSendError` carrying the status, so you
can tell a bad key from a spent quota without parsing strings.

```python
from densend import DenSendError

try:
    densend.Emails.send({...})
except DenSendError as error:
    if error.status == 401:
        raise RuntimeError("Check DENSEND_API_KEY") from error
    if error.is_retryable:
        queue_for_later()
    else:
        raise
```

`is_retryable` is true for 429 and 5xx. A 422 means the payload was wrong
and retrying it unchanged will fail the same way.

## Options

```python
densend.base_url = "https://api.densend.com/v1"  # override when self-hosting
```

`base_url` also reads from the `DENSEND_BASE_URL` environment variable.

## Sending is asynchronous

`send` returns once DenSend has accepted the message, not once it has
landed. The returned id is how you follow it afterwards, through
`Emails.get` or a webhook.
