Skip to main content

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


Download files

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

Source Distribution

truncus-0.1.0.tar.gz (8.8 kB view details)

Uploaded Source

Built Distribution

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

truncus-0.1.0-py3-none-any.whl (7.3 kB view details)

Uploaded Python 3

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

Hashes for truncus-0.1.0.tar.gz
Algorithm Hash digest
SHA256 90fd5dcbf9ad8b72a22d07b0ecf4ab81e61673d481bc961dbc46f28931e58924
MD5 ab61119840a6fff7f2e6654257d8198e
BLAKE2b-256 00ff69084eaec192ac29e66c3a0379cc7c7246035d362cda3e84b901e2e7209b

See more details on using hashes here.

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

Hashes for truncus-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 18e6e8cee4970860b3a262c5db8d2ae70b4065af2f7541d2c378c333af67de5f
MD5 389ac7210ecd087c807531fa0c08625b
BLAKE2b-256 15e76a71e62930ca79413e19f053ba13c3d55aec2abc02f99d79998b6e34b6b4

See more details on using hashes here.

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