Skip to main content

mailtea (Python)

The official Python SDK for Mailtea — a thin, zero-dependency wrapper over the REST API. Python 3.9+.

Install

pip install mailtea

Usage

import os
from mailtea import Mailtea

mailtea = Mailtea(os.environ["MAILTEA_API_KEY"])

sent = mailtea.emails.send(
    from_="you@yourdomain.com",
    to="recipient@example.com",
    subject="Hello from Mailtea",
    html="<p>Your first email, sent with <strong>Mailtea</strong>.</p>",
)
print(sent.id)

email = mailtea.emails.get(sent.id)
print(email.status)

The API key can be passed to Mailtea(api_key) or read from MAILTEA_API_KEY. Self-hosting or local dev? pass base_url= or set MAILTEA_API_BASE_URL.

Every method equally accepts a single wire-format dict — handy when you already hold the JSON payload:

sent = mailtea.emails.send({
    "from": "you@yourdomain.com",
    "to": "recipient@example.com",
    "subject": "Hello from Mailtea",
    "html": "<p>Your first email, sent with <strong>Mailtea</strong>.</p>",
})
print(sent["id"])  # responses support ["..."] and attribute access alike

Keyword arguments use snake_case wire names; a trailing underscore escapes Python reserved words (from_= → "from").

API

Method Description
emails.send(params) Send a transactional email → {"id": ...}
emails.batch(emails) Send up to 100 emails → {"data": [{"id": ...}]}
emails.get(id) Retrieve an email and its delivery status
emails.list(params=None) List emails → {"data", "total", "limit", "offset", "has_more"}. Pass mode="test" for test-mode mail
emails.update(id, params) Reschedule a scheduled email
emails.reschedule(id, scheduled_at) Convenience wrapper over update
emails.cancel(id) Cancel a scheduled email
emails.analytics(params=None) Aggregate transactional metrics over an optional date window
emails.inbound.list(params=None) List received emails in a publication (cursor-paginated)
emails.inbound.get(id) Retrieve a received email with body, headers, and attachments
emails.inbound.reply(id, params=None) Reply to a received email (threads by construction)
emails.inbound.attachments.list(id) List a received email's attachments (signed download URLs)
emails.inbound.attachments.get(id, attachment_id) Retrieve one inbound attachment
contacts.create / upsert / list / get / update / delete Manage audience contacts (upsert = create; the endpoint upserts)
posts.create(...) Create a newsletter post (draft, or send=True) → {"id": ...}
posts.send(id, scheduled_at=None) Send a draft post to the audience, now or scheduled
posts.send_test(id, params) Send a [TEST] copy of a post → {"sent_to", "failed_to"}
posts.list(params) List posts (offset-paginated) → {"data", "total"}
posts.get(id, params=None) Retrieve a post by id
posts.update(id, params) Update a draft post (subject, html, text, from, reply_to, name)
posts.delete(id, params=None) Delete a draft post
segments.create / list / get / update / delete Manage audience segments
topics.create / list / get / update / delete Manage topic definitions (visibility="public" → shown on the reader preference page)
senders.create / list / get / update / delete Manage named From identities (email immutable)
templates.create / list / get / update / publish / duplicate / delete Manage reusable email templates
templates.render(params) Render a spec to HTML without saving → {"html", "text"}
templates.versions(id, params=None) List a template's design history, newest first (metadata only)
templates.restore_version(id, version, params=None) Put an older design back — a content write, so the template returns to draft
suppressions.list / add / remove Manage the team-wide do-not-send list
suppressions.export() Export the whole suppression list as CSV (raw text)
domains.create / list / get / verify / update / delete Manage sending domains (add, read DNS records, verify)
domains.tracking.create / list / verify / delete Manage CNAME tracking sub-domains under a domain
webhooks.create / list / get / update / delete Manage outbound event subscriptions
contact_properties.create / list / update / delete Manage custom contact fields (team-scoped)
api_keys.create / list / revoke Manage API keys (settings:write). create(mode="test") mints a test key
automations.create / list / get / update / delete Manage automation graphs (steps + optional connections)
automations.validate(params) Dry-run a graph → {"valid", "issues"} (no automation needed)
automations.activate / pause / archive Lifecycle (cancel_runs defaults false on pause, true on archive)
automations.versions(id, ...) / automations.version(id, version, ...) List stored versions; retrieve one with its graph
automations.metrics(id, params=None) Per-step funnel counts and branch splits (test runs excluded)
automations.test(id, params) One test run against a real contact — sends real, billed email
automation_runs.list / get / cancel Inspect and cancel runs (a run pins the version it started on)
events.send(params) Record a custom event → {"enrolled_automations", "resumed_runs"}
events.list(params) List recorded events (cursor-paginated)
event_definitions.create / list / get / update / delete Manage the event catalog (name immutable)

Payloads follow the REST wire format (reply_to, scheduled_at, …), passed as keyword arguments or a plain dict. Errors raise MailteaError with status, details, and request_id.

emails.send also accepts tags, custom headers, attachments, and scheduled_at. Attachments carry base64 content; set a content_id (plus content_type) to embed an inline image referenced by cid: in the HTML:

mailtea.emails.send(
    from_="you@yourdomain.com",
    to="recipient@example.com",
    subject="Your receipt",
    html='<p>Thanks!</p><img src="cid:logo" />',
    tags=[{"name": "category", "value": "receipt"}],
    attachments=[
        {"filename": "receipt.pdf", "content": pdf_base64},
        {"filename": "logo.png", "content": logo_base64,
         "content_type": "image/png", "content_id": "logo"},  # inline
    ],
)

Test mode

A test key (mt_test_…) sends nothing. Every message it creates is validated, recorded and emits webhooks, but is never handed to a provider — so CI can point at production Mailtea with your real code and your real webhook handler.

key = client.api_keys.create(name="CI", mode="test")
# key["token"] starts with mt_test_ and key["mode"] == "test"

test_client = Mailtea(key["token"])
test_client.emails.send(
    from_="you@yourdomain.com",
    to="bounced@test.mailtea.email",
    subject="Bounce handling",
    html="<p>Never delivered.</p>",
)

page = test_client.emails.list(mode="test")

Reserved recipients on test.mailtea.email force the outcome — delivered@, bounced@, complained@, delayed@, failed@ — and the first to recipient decides. Every email carries mode, and a test key reads only test mail while a live key reads only live mail; there is no mixed view.

A test key is not a data sandbox. It reads and writes your real contacts, templates, senders and webhooks. Only delivery is simulated.

Verifying webhooks

Mailtea signs every outbound webhook with Standard Webhooks. verify_webhook_signature checks the signature and rejects replays. Pass the raw request body (not re-serialized JSON) and the endpoint's whsec_… signing secret:

from mailtea import verify_webhook_signature

ok = verify_webhook_signature(
    secret=signing_secret,                       # whsec_… from webhooks.create
    msg_id=request.headers["webhook-id"],
    timestamp=request.headers["webhook-timestamp"],
    payload=raw_body,                            # exact bytes/string received
    signature_header=request.headers["webhook-signature"],
)
if not ok:
    return 401

sign_webhook(secret, msg_id, timestamp, payload) produces the same header, handy for faking deliveries in tests. Both are stdlib-only.

Develop

cd sdks/python
python3 -m unittest discover -s tests -t .

Metadata

Release files for mailtea 0.12.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mailtea 0.12.0
File Size Uploaded Provenance
mailtea-0.12.0.tar.gz 42.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mailtea 0.12.0
File Interpreter ABI Platform
mailtea-0.12.0-py3-none-any.whl Python 3 none any Details

Total release size: 79.0 kB

Release files / mailtea-0.12.0.tar.gz

Download URL mailtea-0.12.0.tar.gz
Size 42.9 kB
Tags Source
SHA-256 checksum
How to use checksums
35bb12abfacee1878c1077fab0f46c0830b4906d7b19ce9dc20873f5440d1f18
BLAKE2b-256 checksum
How to use checksums
573fbc23fb2323c79f38e2689e55cd1e69b0c5b21000d2b14ae4982b91f50932
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Release files / mailtea-0.12.0-py3-none-any.whl

Download URL mailtea-0.12.0-py3-none-any.whl
Size 36.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f5648bd715322112916ea72e0a697f2ca75d2fdb77a44d3f6e0f860bc6d5eefc
BLAKE2b-256 checksum
How to use checksums
d298320a6d515bfd0efe4e160ca14f6ecfc62788ed69c534bd636e8bce879462
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Security for mailtea 0.12.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for mailtea-0.12.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page