Skip to content

Repository files navigation

Lettermint Python SDK

PyPI VersionPyPI DownloadsPython VersionGitHub TestsLicenseJoin our Discord server

Official Python SDK for the Lettermint sending and team APIs.

Installation

pip install lettermint

Quick Start

Sending Emails (Synchronous)

fromlettermintimportLettermintemail=Lettermint.email("your-sending-token")
response= (
email
.from_("sender@example.com")
.to("recipient@example.com")
.subject("Hello from Python!")
.html("<h1>Welcome!</h1>")
.text("Welcome!")
.send()
)
print(response["message_id"])

Sending Emails (Asynchronous)

fromlettermintimportAsyncLettermintemail=AsyncLettermint.email("your-sending-token")
response=await (
email
.from_("sender@example.com")
.to("recipient@example.com")
.subject("Hello from Python!")
.html("<h1>Welcome!</h1>")
.send()
)
print(response["message_id"])

The legacy constructor still works for sending-only usage:

client=Lettermint(api_token="your-sending-token")
client.email.from_("sender@example.com").to("recipient@example.com").subject("Hello").send()

Email Options

Multiple Recipients

client.email.from_("sender@example.com").to(
"recipient1@example.com",
"recipient2@example.com"
).subject("Hello").send()

CC and BCC

client.email.from_("sender@example.com").to("recipient@example.com").cc(
"cc1@example.com",
"cc2@example.com"
).bcc("bcc@example.com").subject("Hello").send()

Reply-To

client.email.from_("sender@example.com").to("recipient@example.com").reply_to(
"reply@example.com"
).subject("Hello").send()

RFC 5322 Addresses

client.email.from_("John Doe <john@example.com>").to(
"Jane Doe <jane@example.com>"
).subject("Hello").send()

Attachments

importbase64# Read and encode your filewithopen("document.pdf", "rb") asf:
content=base64.b64encode(f.read()).decode()
# Regular attachmentclient.email.from_("sender@example.com").to("recipient@example.com").subject(
"Your Document"
).attach("document.pdf", content).send()
# Inline attachment (for embedding in HTML)client.email.from_("sender@example.com").to("recipient@example.com").subject(
"Welcome"
).html('<img src="cid:logo@example.com">').attach(
"logo.png", logo_content, "logo@example.com"
).send()

Custom Headers

client.email.from_("sender@example.com").to("recipient@example.com").subject(
"Hello"
).headers({"X-Custom-Header": "value"}).send()

Metadata and Tags

client.email.from_("sender@example.com").to("recipient@example.com").subject(
"Hello"
).metadata({"campaign_id": "123", "user_id": "456"}).tag("welcome-campaign").send()

Routing

client.email.from_("sender@example.com").to("recipient@example.com").subject(
"Hello"
).route("my-route").send()

Idempotency Key

Prevent duplicate sends when retrying failed requests:

client.email.from_("sender@example.com").to("recipient@example.com").subject(
"Hello"
).idempotency_key("unique-request-id").send()

Batch Sending

email=Lettermint.email("your-sending-token")
response=email.send_batch([
{
"from": "sender@example.com",
"to": ["recipient@example.com"],
"subject": "Hello from Python!",
"text": "This is a batch email.",
}
])

Both sync and async sending clients support ping():

email.ping()
awaitAsyncLettermint.email("your-sending-token").ping()

Team API

Use a team API token with Lettermint.api(...). API tokens authenticate with Authorization: Bearer ... and are separate from project sending tokens.

fromlettermintimportLettermintapi=Lettermint.api("your-api-token")
domains=api.domains.list({"page[size]": "10"})
team=api.team.retrieve()
message_html=api.messages.html("message-id")
pong=api.ping()

The async Team API client is available through AsyncLettermint.api(...):

fromlettermintimportAsyncLettermintapi=AsyncLettermint.api("your-api-token")
domains=awaitapi.domains.list({"page[size]": "10"})
message_html=awaitapi.messages.html("message-id")

Endpoint groups are available as domains, messages, projects, routes, stats, suppressions, team, and webhooks.

Webhook Verification

Verify webhook signatures to ensure authenticity:

fromlettermintimportWebhook# Create a webhook verifierwebhook=Webhook(secret="your-webhook-secret")
# Verify using headers (recommended)payload=webhook.verify_headers(request.headers, request.body)
# Or verify using the signature directlypayload=webhook.verify(
payload=request.body,
signature=request.headers["X-Lettermint-Signature"],
)
print(payload["event"])

Static Method

For one-off verification:

fromlettermintimportWebhookpayload=Webhook.verify_signature(
payload=request.body,
signature=request.headers["X-Lettermint-Signature"],
secret="your-webhook-secret",
)

Custom Tolerance

Adjust the timestamp tolerance (default: 300 seconds):

webhook=Webhook(secret="your-webhook-secret", tolerance=600)

Error Handling

fromlettermintimportLettermintfromlettermint.exceptionsimport (
ValidationError,
ClientError,
HttpRequestError,
TimeoutError,
)
client=Lettermint(api_token="your-sending-token")
try:
response=client.email.from_("sender@example.com").to("recipient@example.com").subject(
"Hello"
).send()
exceptValidationErrorase:
# 422 errors (e.g., daily limit exceeded)print(f"Validation error: {e.error_type}")
print(f"Response: {e.response_body}")
exceptClientErrorase:
# 400 errorsprint(f"Client error: {e}")
exceptTimeoutErrorase:
# Request timeoutprint(f"Timeout: {e}")
exceptHttpRequestErrorase:
# Other HTTP errorsprint(f"HTTP error {e.status_code}: {e}")

Webhook Errors

fromlettermintimportWebhookfromlettermint.exceptionsimport (
InvalidSignatureError,
TimestampToleranceError,
JsonDecodeError,
WebhookVerificationError,
)
try:
payload=webhook.verify_headers(headers, body)
exceptInvalidSignatureError:
print("Invalid signature - request may be forged")
exceptTimestampToleranceError:
print("Timestamp too old - possible replay attack")
exceptJsonDecodeError:
print("Invalid JSON in payload")
exceptWebhookVerificationErrorase:
print(f"Verification failed: {e}")

Configuration

Custom Base URL

client=Lettermint(
api_token="your-sending-token",
base_url="https://custom.api.com/v1",
)

Custom Timeout

client=Lettermint(
api_token="your-sending-token",
timeout=60.0, # 60 seconds
)

Context Manager

Both sync and async clients support context managers for proper resource cleanup:

# SyncwithLettermint(api_token="your-sending-token") asclient:
client.email.from_("sender@example.com").to("recipient@example.com").send()
# AsyncasyncwithAsyncLettermint(api_token="your-sending-token") asclient:
awaitclient.email.from_("sender@example.com").to("recipient@example.com").send()

Type Hints

This SDK is fully typed with py.typed marker. You'll get full autocomplete and type checking in your IDE.

Requirements

  • Python 3.9+
  • httpx

License

MIT

About

No description, website, or topics provided.

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages