Official Python SDK for Truncus Email
Project description
truncus
Python SDK for Truncus — transactional email infrastructure built on AWS SES with deliverability built-in.
Installation
pip install truncus
Quick Start
from truncus import Truncus
client = Truncus(api_key="tr_live_...")
result = client.send(
to="user@example.com",
from_="hello@mail.yourdomain.com",
subject="Welcome!",
html="<h1>Hello World</h1>",
)
print(result.id) # Email ID
print(result.status) # 'sent'
Configuration
client = Truncus(
api_key="tr_live_...", # Required
base_url="https://truncus.co", # Optional, defaults to production
timeout=30, # Optional, request timeout in seconds (default 30)
sandbox=False, # Optional, enable sandbox mode globally (default False)
)
Sandbox mode
Sandbox mode validates payloads, checks domain ownership, suppression lists, and rate limits — but never delivers the email.
# Enable globally for all calls
client = Truncus(api_key="tr_live_...", sandbox=True)
result = client.send(to=..., from_=..., subject=..., html="...")
# result.status == "sandbox"
# Or use validate() for a one-off dry-run (same params as send())
result = client.validate(
to="user@example.com",
from_="hello@mail.yourdomain.com",
subject="Test",
html="<p>Hi</p>",
)
# result.status == "sandbox"
API Reference
send()
Send a transactional email.
result = client.send(
# Required
to="user@example.com",
from_="hello@mail.yourdomain.com", # Must match a verified domain
subject="Welcome!",
html="<h1>Hello World</h1>",
# Optional
text="Hello World", # Plain-text fallback
cc=["cc@example.com"],
bcc=["bcc@example.com"],
template_id="tmpl_...", # Use a saved template instead of html
variables={"name": "John"}, # Template variables ({{name}})
metadata={"user_id": "123"}, # Custom metadata (stored, not sent)
idempotency_key="order-123", # Prevent duplicate sends
tenant_id="tenant_abc", # Multi-tenant suppression isolation
send_at="2026-03-10T09:00:00Z", # Schedule for future delivery (ISO 8601)
track_opens=True, # 1×1 tracking pixel (default: True)
track_clicks=True, # Rewrite links through click proxy (default: True)
attachments=[
{
"filename": "invoice.pdf",
"content": "<base64-encoded-content>",
"content_type": "application/pdf",
}
],
)
# result.status == 'sent' — sent immediately
# result.status == 'scheduled' — queued for send_at time
# result.send_at — ISO 8601 delivery time (scheduled emails)
get_email(email_id)
Retrieve details for a specific email, including open and click tracking stats.
email = client.get_email("email_id")
print(email.status) # 'delivered'
print(email.open_count) # 3 (total opens, including repeats)
print(email.opened_at) # '2026-03-04T10:23:00Z' (first open)
print(email.click_count) # 1
batch(emails)
Send up to 100 emails in a single request.
result = client.batch([
{
"to": "user1@example.com",
"from_": "hello@mail.yourdomain.com",
"subject": "Hi",
"html": "<p>Hi User 1</p>",
},
{
"to": "user2@example.com",
"from_": "hello@mail.yourdomain.com",
"subject": "Hi",
"html": "<p>Hi User 2</p>",
},
])
print(result.sent) # 2
print(result.failed) # 0
Open & Click Tracking
Tracking is enabled by default. Truncus injects a 1×1 pixel for opens and rewrites links for click tracking. Unsubscribe links are never rewritten.
# Default — both opens and clicks tracked
result = client.send(to=..., from_=..., subject=..., html="...")
# Disable tracking
result = client.send(
to=..., from_=..., subject=..., html="...",
track_opens=False,
track_clicks=False,
)
# Check engagement after sending
email = client.get_email(result.id)
print(email.opened_at) # First open timestamp (None if not opened)
print(email.open_count) # Total opens
print(email.click_count) # Total link clicks
Scheduled Sending
result = client.send(
to="user@example.com",
from_="hello@mail.yourdomain.com",
subject="Your weekly digest",
html="...",
send_at="2026-03-10T09:00:00Z", # Must be a future datetime
)
print(result.status) # 'scheduled'
print(result.send_at) # '2026-03-10T09:00:00Z'
Idempotency
Prevent duplicate sends with a stable idempotency key. Retrying with the same key returns the original response without sending again:
result = client.send(
to="user@example.com",
from_="hello@mail.yourdomain.com",
subject="Order Confirmation",
html="...",
idempotency_key=f"order-{order_id}-confirmation",
)
A UUID v4 is auto-generated when not provided.
Multi-tenant Apps
Isolate suppression lists per tenant:
result = client.send(
to="user@example.com",
from_="hello@mail.yourdomain.com",
subject="Welcome!",
html="...",
tenant_id="tenant_123",
)
Error Handling
from truncus import TruncusError
try:
client.send(...)
except TruncusError as e:
print(e.code) # 'DOMAIN_NOT_VERIFIED'
print(str(e)) # 'Domain is not verified'
print(e.status) # 400
Error Codes
| Code | Status | Description |
|---|---|---|
MISSING_API_KEY |
401 | Authorization header missing |
INVALID_API_KEY |
401 | API key is invalid or revoked |
SCOPE_REQUIRED |
403 | API key missing required scope |
INVALID_REQUEST |
400 | Request body validation failed |
DOMAIN_NOT_FOUND |
404 | Domain doesn't exist |
DOMAIN_NOT_VERIFIED |
400 | Domain pending DNS verification |
DOMAIN_PAUSED |
400 | Domain paused due to high bounce/complaint rate |
WARMUP_CAP_EXCEEDED |
429 | Daily sending limit reached |
ALL_RECIPIENTS_SUPPRESSED |
422 | All recipients are on suppression list |
PROVIDER_ERROR |
502 | AWS SES returned an error |
TIMEOUT |
408 | Request timed out |
NETWORK_ERROR |
0 | Network connectivity issue |
License
MIT
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 truncus-0.1.0.tar.gz.
File metadata
- Download URL: truncus-0.1.0.tar.gz
- Upload date:
- Size: 8.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90fd5dcbf9ad8b72a22d07b0ecf4ab81e61673d481bc961dbc46f28931e58924
|
|
| MD5 |
ab61119840a6fff7f2e6654257d8198e
|
|
| BLAKE2b-256 |
00ff69084eaec192ac29e66c3a0379cc7c7246035d362cda3e84b901e2e7209b
|
File details
Details for the file truncus-0.1.0-py3-none-any.whl.
File metadata
- Download URL: truncus-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
18e6e8cee4970860b3a262c5db8d2ae70b4065af2f7541d2c378c333af67de5f
|
|
| MD5 |
389ac7210ecd087c807531fa0c08625b
|
|
| BLAKE2b-256 |
15e76a71e62930ca79413e19f053ba13c3d55aec2abc02f99d79998b6e34b6b4
|