Skip to main content

Vanty App: ESP-agnostic transactional mail, templates, webhooks.

Project description

Vanty Mail

PyPI Python

ESP-agnostic transactional mail toolkit for FastAPI with Tortoise ORM. One typed MailApp API, swappable backends (Resend, SMTP, Mailgun, SendGrid, Postmark, Memory), Jinja2 templates with safe filters, normalized provider webhooks, and tenant-scoped storage.

Inspired by django-anymail — re-implemented for FastAPI + Tortoise ORM + httpx with vanty-core events.

Installation

pip install vanty-mail
# or
uv pip install vanty-mail

Quick start

from contextlib import asynccontextmanager

from fastapi import FastAPI

from vanty_mail import MailSettings, mount_mail_router

settings = MailSettings(
    database_url="sqlite://./vanty-mail.db",
    default_backend="resend",
    resend_api_key="re_...",
    default_from_email="hello@example.com",
)

app = FastAPI()
kit = mount_mail_router(app, settings=settings)
app.include_router(kit.admin_router, prefix="/admin/mail")


@asynccontextmanager
async def lifespan(_: FastAPI):
    await kit.init_orm(generate_schemas=True)
    try:
        yield
    finally:
        await kit.close_orm()


app.router.lifespan_context = lifespan

Sending mail

# Direct send
from vanty_mail import EmailMessage

await kit.mail_service.send(
    EmailMessage(
        to=["alice@example.com"],
        subject="Welcome",
        html="<h1>Hi Alice</h1>",
        text="Hi Alice",
    ),
)

# Template send (uses an EmailTemplate row)
await kit.mail_service.send_template(
    "welcome",
    to=["alice@example.com"],
    context={"name": "Alice"},
    organization_id=org_id,
)

ESP switching cookbook

Pick a backend at runtime via the backend= kwarg, or switch the default in MailSettings.default_backend. Each backend reads its own creds from settings (or from the encrypted ESPSetting rows in the DB).

Backend default_backend Required settings
Resend resend resend_api_key, optional resend_webhook_secret
SMTP smtp smtp_host, smtp_port, smtp_username, smtp_password
Memory memory (test-only; records sent messages on the backend instance)
Mailgun mailgun stub — raises NotImplementedError
SendGrid sendgrid stub — raises NotImplementedError
Postmark postmark stub — raises NotImplementedError
await kit.mail_service.send(message, backend="smtp")

The Mailgun, SendGrid, and Postmark backends are intentionally stubbed — they follow the same interface and raise NotImplementedError with a clear hint (e.g. "set MAILGUN_API_KEY ..."). Drop in your own implementation by subclassing EmailBackend and registering with register_backend("mailgun", ...).

Webhooks

Mount once and provider events become normalized vanty_core.events:

POST /mail/webhooks/resend
POST /mail/webhooks/mailgun
...

Each provider's payload is parsed into the same WebhookEvent shape and the matching domain event is published (vanty_mail.mail.delivered, .opened, .clicked, .bounced, .complained). Subscribe with from vanty_core.events import on.

Encryption

ESP credentials and webhook secrets are encrypted at rest with Fernet from the cryptography package. Set MAIL_ENCRYPTION_KEY (a urlsafe base64-encoded 32-byte key) in your environment. If unset, an ephemeral key is generated in-process and a loud warning is logged — values written to the DB will be unreadable on restart, so this mode is only useful for tests.

Generate a key with:

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Events

All published via vanty_core.events.publish:

  • vanty_mail.mail.sent
  • vanty_mail.mail.delivered
  • vanty_mail.mail.opened
  • vanty_mail.mail.clicked
  • vanty_mail.mail.bounced
  • vanty_mail.mail.complained
  • vanty_mail.mail.template_changed
  • vanty_mail.mail.inbound_received

Publishing to GitHub

This package lives inside the Vanty monorepo but ships as a standalone repo. Use the bundled Makefile target:

make publish-github REPO=git@github.com:advantch/vanty-mail.git

Requires git-filter-repo (pip install git-filter-repo). The target clones the monorepo to a temp directory, filters history down to vanty-mail/, adds the new remote, and pushes — your local monorepo checkout is never modified.

Known limitations

  • The Mailgun, SendGrid, and Postmark backends are stubs. They raise NotImplementedError with a clear hint — implement and register your own to enable.
  • The default Tortoise SQLite driver is fine for tests; use Postgres in production for OrganizationScopedModel to behave correctly under load.
  • The MAIL_ENCRYPTION_KEY ephemeral fallback emits a warning every process start; do not rely on it outside of tests.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vanty_mail-0.3.0.tar.gz (109.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vanty_mail-0.3.0-py3-none-any.whl (31.3 kB view details)

Uploaded Python 3

File details

Details for the file vanty_mail-0.3.0.tar.gz.

File metadata

  • Download URL: vanty_mail-0.3.0.tar.gz
  • Upload date:
  • Size: 109.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vanty_mail-0.3.0.tar.gz
Algorithm Hash digest
SHA256 6e42aba6919a1454b03aac381f5bae2d4fa2ec2f9f67cabd4620ef0fc4cc2ddd
MD5 adca1583d6de502f3bc4a30114e55c79
BLAKE2b-256 d6591e289a5a05df91df9c86c541b8305b499404e21ad0b1bdd9897af55ab4b2

See more details on using hashes here.

Provenance

The following attestation bundles were made for vanty_mail-0.3.0.tar.gz:

Publisher: release.yml on advantch/vanty-mail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vanty_mail-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: vanty_mail-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 31.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vanty_mail-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 940f09895304ed43f5b3601bd50f7c10f46ca23a1003fa34871eb882d455f598
MD5 0c4afbbbaa966e80e24ead71ff03d419
BLAKE2b-256 f53256058dec29757bc56c0956c6d830e985482684ed88c9b8a5dddf8e523b68

See more details on using hashes here.

Provenance

The following attestation bundles were made for vanty_mail-0.3.0-py3-none-any.whl:

Publisher: release.yml on advantch/vanty-mail

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page