Mailkube Python SDK
Project description
mailkube
The official Python SDK for Mailkube — send transactional email and verify inbound webhooks. Fully typed, sync and async, Python 3.12+.
Full product and API documentation: mailkube.com/docs.
Install
pip install mailkube
# or
uv add mailkube
Configuration
from mailkube import Mailkube
client = Mailkube(
api_key="mk_...", # or set MAILKUBE_API_KEY
base_url="https://api.mailkube.com/mta/v1/", # or set MAILKUBE_BASE_URL; this is the default
timeout=30.0, # per-request timeout in seconds
)
AsyncMailkube takes the same arguments. Both also accept http_client= — your own
httpx.Client / httpx.AsyncClient — if you need custom transport, proxies, or mTLS; the SDK
will not close a client you pass in.
Send an email
from mailkube import Mailkube
with Mailkube() as client:
email = client.emails.send(
from_="Acme <hello@yourdomain.com>",
to="customer@example.com",
cc="manager@example.com",
reply_to="support@yourdomain.com",
subject="Hello world",
html="<p>It works!</p>",
)
print(email.id, email.message_id)
from_ maps to the wire from field (from is a reserved keyword). Supply html and/or text, or
a template_id with template_version and variables. Attachments accept raw bytes or a base64
string. See SendEmailParams for the full field list (cc, bcc,
reply_to, headers, attachments, template_id, topic, idempotency_key, …).
Async
import asyncio
from mailkube import AsyncMailkube
async def main():
async with AsyncMailkube() as client:
email = await client.emails.send(
from_="Acme <hello@yourdomain.com>",
to="customer@example.com",
subject="Hello world",
html="<p>It works!</p>",
)
print(email.id)
asyncio.run(main())
Idempotency
Pass idempotency_key to safely retry a send without risking a duplicate — sent as the
Idempotency-Key header, not in the body:
email = client.emails.send(
from_="Acme <hello@yourdomain.com>",
to="customer@example.com",
subject="Your receipt",
html="<p>Thanks for your order.</p>",
idempotency_key="order-4821-receipt",
)
print(email.idempotent_replayed) # True if this key was already used
Reusing a key with a different payload raises ConflictError instead of silently sending the new
content.
Errors
Every failure raises a subclass of MailkubeError (AuthenticationError, InvalidRequestError,
RateLimitError — which carries .retry_after — ServerError, MailkubeConnectionError, …). Each
API error exposes .error_name, .message, and .status_code.
Threading
Echo a message's message_id in the In-Reply-To / References headers of a later send:
reply = client.emails.send(
from_="Acme <hello@yourdomain.com>",
to="customer@example.com",
subject="Re: Your order",
html="<p>An update.</p>",
headers={"In-Reply-To": first.message_id, "References": first.message_id},
)
Verify webhooks
webhooks.verify is a pure, stdlib-only helper — call it in your request handler with the raw body:
from mailkube import verify, SignatureVerificationError, UnknownEvent
try:
event = verify(raw_body, request.headers, signing_secret)
except SignatureVerificationError:
... # reject with 400
if isinstance(event, UnknownEvent):
... # a newer event type than this SDK version knows about — still usable
else:
print(event.type)
An unrecognized event type is returned as UnknownEvent instead of raising, so a new server event
type never forces an SDK upgrade on receivers.
Logging
Silent by default. Turn on request/response logging with:
import mailkube
mailkube.enable_logging(level="DEBUG")
or set the MAILKUBE_LOG environment variable. Authorization and Idempotency-Key headers are
redacted from log output.
Client lifecycle
Create one client and reuse it. Mailkube is thread-safe; AsyncMailkube is bound to its event
loop. Use the client as a (async) context manager, or call .close() / .aclose().
More examples
Runnable scripts in examples/:
simple_send.py— basic sync sendasync_send.py— async send, then thread a reply onto itsend_with_attachments.py— attach a file from raw bytessend_with_template.py— send from a saved templatewebhook_receiver_flask.py— verify and dispatch webhooks in a Flask app
Contributing
See CONTRIBUTING.md for the development setup and the quality gates every change must pass. Security issues: see SECURITY.md.
License
Apache-2.0 © 2026 Mailtactic, Corp.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mailkube-1.1.0.tar.gz.
File metadata
- Download URL: mailkube-1.1.0.tar.gz
- Upload date:
- Size: 70.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acbfec054962bdafb8e62da21e0ceaf3f0b62d3227da3c6d23e9a9376ad8e8f6
|
|
| MD5 |
f98edb58b97cc971737d8b148f38dbba
|
|
| BLAKE2b-256 |
4e32c08bc4988fc04501624dd9148bdba17f180e88a1460cb24029f8e2bdcbfd
|
Provenance
The following attestation bundles were made for mailkube-1.1.0.tar.gz:
Publisher:
release.yml on mailkube/mailkube-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mailkube-1.1.0.tar.gz -
Subject digest:
acbfec054962bdafb8e62da21e0ceaf3f0b62d3227da3c6d23e9a9376ad8e8f6 - Sigstore transparency entry: 2190183891
- Sigstore integration time:
-
Permalink:
mailkube/mailkube-python@081e81eac5243d7729751c0b0fc2b949303fef96 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mailkube
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@081e81eac5243d7729751c0b0fc2b949303fef96 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mailkube-1.1.0-py3-none-any.whl.
File metadata
- Download URL: mailkube-1.1.0-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72dc90a6612ee327cf23db71a77ab6d80be6b4a6682d89bd69aeb80d9b169eba
|
|
| MD5 |
38bb7926e9bee343c112d8be2e02a630
|
|
| BLAKE2b-256 |
4f375576a2f12d76c95fac1a687daecba50077408d0ec333278fd641e377360b
|
Provenance
The following attestation bundles were made for mailkube-1.1.0-py3-none-any.whl:
Publisher:
release.yml on mailkube/mailkube-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mailkube-1.1.0-py3-none-any.whl -
Subject digest:
72dc90a6612ee327cf23db71a77ab6d80be6b4a6682d89bd69aeb80d9b169eba - Sigstore transparency entry: 2190183924
- Sigstore integration time:
-
Permalink:
mailkube/mailkube-python@081e81eac5243d7729751c0b0fc2b949303fef96 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/mailkube
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@081e81eac5243d7729751c0b0fc2b949303fef96 -
Trigger Event:
push
-
Statement type: