Skip to content

Repository files navigation

altissimo-customerio

CI Python License Code style: ruff

Customer.io transactional email and messaging client for Altissimo Python projects.

Features

  • πŸ“‹ Template emails via send_email() β€” uses Customer.io transactional templates
  • πŸ“§ Plain text emails via send_text() β€” inline content, no template required
  • 🎨 HTML emails via send_html() β€” inline content, no template required
  • 🌍 Region support β€” US and EU data centers
  • πŸ—οΈ Factory method β€” CustomerIOClient.from_env() reads CUSTOMERIO_API_KEY from the environment
  • πŸ”Œ Optional dependency β€” customerio SDK is lazily imported with a helpful error message
  • πŸ“ Typed β€” full type annotations with py.typed PEP 561 marker
  • βœ… Consistent return type β€” all methods return a SendResult dataclass
  • πŸ”„ Retry with backoff β€” configurable retry for transient failures
  • πŸ”” Fails loudly β€” send failures raise CustomerIOSendError by default; opt out per client for batch sends

Requirements

  • Python 3.11+

Installation

pip install altissimo-customerio[customerio]   # core + Customer.io SDK

Quick Start

from altissimo.customerio import CustomerIOClient

client = CustomerIOClient.from_env()

# Template email (most common)
result = client.send_email(
    to="user@example.com",
    transactional_message_id="3",
    message_data={"first_name": "Alice", "year": 2026},
)

# Plain text
result = client.send_text(
    to="user@example.com",
    subject="Hello",
    body="Welcome aboard!",
)

# HTML
result = client.send_html(
    to="user@example.com",
    subject="Hello",
    html="<h1>Welcome!</h1>",
    reply_to="support@example.com",
)

print(result.delivery_id)

Error Handling

By default a failed send raises CustomerIOSendError. The originating SDK exception is preserved as __cause__, and the full SendResult is attached as .result:

from altissimo.customerio import CustomerIOClient, CustomerIOSendError

try:
    result = client.send_html(to="user@example.com", subject="Hi", html="<p>Hi</p>")
except CustomerIOSendError as exc:
    logger.error("Reset email failed (status=%s): %s", exc.status_code, exc)
else:
    logger.info("Reset email sent: delivery_id=%s", result.delivery_id)

For batch sends where one bad recipient should not abort the run, opt out and check result.ok yourself:

client = CustomerIOClient.from_env(raise_on_error=False)

for user in users:
    result = client.send_email(to=user.email, transactional_message_id="3")
    if not result.ok:
        failures.append((user.email, result.status_code, result.error))

SendResult.raise_for_status() converts a result to an exception on demand (mirroring requests.Response.raise_for_status), which is handy when you want swallow semantics in one place and raise semantics in another:

client.send_html(to=..., subject=..., html=...).raise_for_status()

Note: the library does not log send failures above DEBUG when raise_on_error=True β€” it has no recipient or template context worth logging from that frame, and a log there would make the failure look handled. Reporting is the caller's job.

Configuration

Environment Variables

Variable Description Default
CUSTOMERIO_API_KEY Customer.io App API key (required)
CUSTOMERIO_REGION Data center region (us or eu) us

Constructor

client = CustomerIOClient(
    app_api_key="your-api-key",
    region="us",                          # "us" or "eu"
    default_from="noreply@lived.com",     # default sender
    max_retries=3,                        # retry transient failures
    retry_delay=1.0,                      # base delay (seconds)
    raise_on_error=True,                  # raise CustomerIOSendError on failure (default)
)

API Reference

CustomerIOClient

Method Description
CustomerIOClient(app_api_key, region?, default_from?, raise_on_error?) Create a client with an explicit API key
CustomerIOClient.from_env(env_var?, region?, default_from?, raise_on_error?) Create a client from an environment variable
send_email(to, transactional_message_id, message_data?, ...) Send a template-based transactional email
send_text(to, subject, body, from_email?, reply_to?, ...) Send a plain-text email (inline content)
send_html(to, subject, html, from_email?, reply_to?, ...) Send an HTML email (inline content)

SendResult

Field Type Description
ok bool Whether the request succeeded
delivery_id str Delivery ID from Customer.io
status_code int HTTP status code (0 on exception)
body dict[str, Any] Response body
error str | None Error message on failure
exception Exception | None Originating SDK exception on failure
Method Description
raise_for_status(context?) Raise CustomerIOSendError if ok is False; no-op otherwise

Exceptions

Exception Raised when
CustomerIOError Base class for all library errors
CustomerIOImportError The customerio SDK is not installed
CustomerIOSendError A send failed (carries status_code and result)

Architecture

altissimo.customerio
β”œβ”€β”€ __init__.py       # Public API surface
β”œβ”€β”€ client.py         # CustomerIOClient with lazy SDK initialization
β”œβ”€β”€ exceptions.py     # CustomerIOError, CustomerIOImportError, CustomerIOSendError
β”œβ”€β”€ models.py         # EmailAddress, SendResult dataclasses
└── py.typed          # PEP 561 marker

Development

# Install all dependencies
poetry sync

# Run tests
poetry run pytest

# Run tests with coverage
poetry run pytest --cov=altissimo --cov-report=term-missing

# Run linters
poetry run ruff check .
poetry run ruff format --check .

See CONTRIBUTING.md for detailed development guidelines.

Changelog

See CHANGELOG.md for version history.

Security

For reporting security vulnerabilities, see SECURITY.md.

License

Apache License 2.0 β€” see LICENSE for details.

About

Customer.io transactional email and messaging python client

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages