Metadata-Version: 2.4
Name: klarient-listmonk
Version: 0.1.1
Summary: A typed Python client for the Listmonk API built with Klarient.
Author-email: Ludvik Jerabek <83429267+ludvikjerabek@users.noreply.github.com>
License-Expression: MIT
Project-URL: Documentation, https://github.com/ludvikjerabek/klarient-listmonk#readme
Project-URL: Issues, https://github.com/ludvikjerabek/klarient-listmonk/issues
Project-URL: Repository, https://github.com/ludvikjerabek/klarient-listmonk
Keywords: api,klarient,listmonk,rest,typed
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: klarient[requests]>=0.2.1
Provides-Extra: httpx
Requires-Dist: klarient[httpx]>=0.2.1; extra == "httpx"
Provides-Extra: dev
Requires-Dist: klarient[dev]>=0.2.1; extra == "dev"
Requires-Dist: pytest>=8; extra == "dev"

# Listmonk API Package

Library implements the Listmonk REST API via Python.

The package is built with Klarient and models Listmonk as a typed resource tree.
The Python object you call matches the URI path in the Listmonk API docs.

### Requirements:

* Python 3.11+
* klarient
* requests

### Installing the Package

You can install the API library using pip.

```
pip install klarient-listmonk
```

For local development from this folder, use an editable install.

```
python3 -m pip install -e .
```

### Creating an API client object

```python
from listmonk import ListMonkClient

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )
```

### Endpoint Examples

Endpoint-focused examples are available under `examples/`.

```text
examples/
  bounces.py
  campaigns.py
  imports.py
  lists.py
  media.py
  public.py
  subscribers.py
  templates.py
  transactional.py
```

Copy `examples/settings.example.json` to `examples/settings.json` and add your Listmonk URL and API credentials to run
them locally.

Most scripts are read-only by default. Actions that create, update, delete, upload, blocklist, or send messages are
guarded by settings flags such as `create_test_data`, `delete_import`, `delete_bounces`, and `delete_uploaded_media`.

### Resource Paths

The API is modeled as a resource tree. Each resource exposes its path and URL, which can be useful when learning or
debugging the wrapper.

```python
from listmonk import ListMonkClient

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )

    print(client.lists.path)
    # /api/lists

    print(client.lists[1].path)
    # /api/lists/1

    print(client.templates[1].preview.path)
    # /api/templates/1/preview
```

### Querying Lists

```python
from listmonk import ListMonkClient
from listmonk.common import PerPage
from listmonk.lists import ListQuery

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )

    lists = client.lists.retrieve(ListQuery().with_per_page(PerPage.ALL))

    print("Total Lists: {}".format(lists.data.total))
    for item in lists.data.results:
        print(item.id)
        print(item.name)
```

### Querying Subscribers

```python
from listmonk import ListMonkClient
from listmonk.common import PerPage, SortOrder
from listmonk.subscribers import SubscriberOrderBy, SubscriberQuery

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )

    query = (
        SubscriberQuery()
        .with_page(1)
        .with_per_page(PerPage.ALL)
        .with_order_by(SubscriberOrderBy.NAME)
        .with_order(SortOrder.ASC)
    )

    subscribers = client.subscribers.retrieve(query)

    print("Total Subscribers: {}".format(subscribers.data.total))
    for subscriber in subscribers.data.results:
        print(subscriber.id)
        print(subscriber.email)
        print(subscriber.name)
```

### Subscriber SQL Query

Listmonk exposes SQL filtering on `GET /api/subscribers`. The wrapper models this as
`client.subscribers.sql_query`.

```python
from listmonk import ListMonkClient
from listmonk.common import PerPage
from listmonk.subscribers import SubscriberSQLQuery

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )

    response = client.subscribers.sql_query.retrieve(
        SubscriberSQLQuery()
        .with_sql("subscribers.id > 0")
        .with_per_page(PerPage.ALL)
    )

    print("SQL Results: {}".format(response.data.total))
```

This requires the `subscribers:sql_query` permission. Listmonk documents that permission as powerful because it can
bypass individual list and subscriber permission boundaries, even though the query is read-only.

### Previewing Templates

```python
from listmonk import ListMonkClient
from listmonk.templates import TemplatePreviewRender, TemplateType

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )

    existing = client.templates[1].preview.retrieve()
    print(existing.data)

    html = """
    <!doctype html>
    <html>
      <body>
        {{ template "content" . }}
        {{ TrackView }}
      </body>
    </html>
    """.strip()

    preview = client.templates.preview.render(
        TemplatePreviewRender(
            type=TemplateType.CAMPAIGN,
            body=html,
        )
    )
    print(preview.data)
```

Listmonk expects `POST /api/templates/preview` to be form encoded, even though template create and update use JSON. The
wrapper hides that detail behind `TemplatePreviewRender`.

### Uploading Media

```python
from listmonk import ListMonkClient
from listmonk.media import MediaUpload

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )

    uploaded = client.media.upload(MediaUpload.from_file("logo.png"))
    print(uploaded.data.id)
    print(uploaded.data.filename)
```

### Sending Transactional Messages

```python
from listmonk import ListMonkClient
from listmonk.transactional import (
    TransactionalContentType,
    TransactionalMessage,
    TransactionalSubscriberMode,
)

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
    )

    message = (
        TransactionalMessage()
        .with_template(1)
        .with_subscriber_mode(TransactionalSubscriberMode.EXTERNAL)
        .add_subscriber_email("user@example.com")
        .with_content_type(TransactionalContentType.HTML)
        .with_data("name", "Jane")
    )

    result = client.tx.send(message)
    print(result.status)
```

### Using A Proxy

`ListMonkClient` defaults to Klarient's requests transport. Proxy settings can be passed through `native_options`, which
are forwarded to the underlying requests call.

```python
from listmonk import ListMonkClient

if __name__ == '__main__':
    client = ListMonkClient(
        base_url="https://listmonk.example.com",
        username="api-user",
        access_token="api-token",
        native_options={
            "proxies": {
                "http": "http://proxy.example.com:8080",
                "https": "http://proxy.example.com:8080",
            },
        },
    )

    lists = client.lists.retrieve()
    print(lists.status)
```

If your environment already uses standard proxy variables, requests can also pick them up automatically.

```bash
export HTTP_PROXY="http://proxy.example.com:8080"
export HTTPS_PROXY="http://proxy.example.com:8080"
```

### Endpoint Coverage

The wrapper models the documented Listmonk API areas:

* Bounces
* Campaigns
* Imports
* Lists
* Media
* Public list and subscription endpoints
* Subscribers
* Templates
* Transactional messages

The transactional API is available as both `client.tx` and `client.transactional`.
