Wraps Logo
DocsHome
SDK Reference

Python Email SDK

Send email through your Wraps-deployed AWS SES infrastructure from Python. Typed, lightweight (built on httpx and botocore signing — no boto3), with a simple synchronous API.

Installation

pip install wraps-email

Requires Python 3.10+

The distribution is wraps-email; import it as wraps.email. Use from_ for the sender — from is a Python keyword.

Quick Start

Pythonexample.py
from wraps.email import WrapsEmailemail = WrapsEmail()result = email.send(    from_="hello@yourdomain.com",    to="user@example.com",    subject="Welcome!",    html="<h1>Hello!</h1>",)print("Message ID:", result.message_id)

Configuration

Pythonclient.py
from wraps.email import WrapsEmail# Default AWS credential chain (env vars, shared config, SSO, OIDC, IMDS)email = WrapsEmail()# Explicit region + static credentialsemail = WrapsEmail(    region="us-west-2",    credentials={        "access_key_id": "...",        "secret_access_key": "...",        "session_token": "...",  # optional    },)# Named AWS profileemail = WrapsEmail(profile="my-profile")# Assume an IAM role (OIDC / cross-account)email = WrapsEmail(role_arn="arn:aws:iam::123456789012:role/MyRole")

Credentials resolve in this order: explicit credentials role_arnprofile → the default AWS chain (env vars, shared config, SSO, OIDC, IMDS).

Sending Emails

Pythonsend.py
result = email.send(    from_="hello@yourdomain.com",    to="user@example.com",    subject="Welcome to our app",    html="<h1>Welcome!</h1><p>Thanks for signing up.</p>",    text="Welcome! Thanks for signing up.",)print("Message ID:", result.message_id)print("Request ID:", result.request_id)
Pythonrecipients.py
result = email.send(    from_="newsletter@yourdomain.com",    to=["user1@example.com", "user2@example.com"],    cc="manager@yourdomain.com",    bcc=["archive@yourdomain.com"],    reply_to="support@yourdomain.com",    subject="Weekly Newsletter",    html="<h1>This week's updates</h1>",)
Pythontags.py
# Add SES tags for tracking and analyticsemail.send(    from_="you@yourdomain.com",    to="user@example.com",    subject="Newsletter",    html="<p>Content</p>",    tags={"campaign": "newsletter-2026-07", "type": "marketing"},    configuration_set_name="wraps-email-tracking",  # optional)

Attachments

Pythonattachments.py
from wraps.email import Attachmentresult = email.send(    from_="hello@yourdomain.com",    to="user@example.com",    subject="Your report",    html="<p>See attached.</p>",    attachments=[        Attachment(            filename="report.csv",            content="date,sends\n2026-07-13,42\n",            content_type="text/csv",        ),        # Binary content works too — pass raw bytes:        Attachment(filename="logo.png", content=open("logo.png", "rb").read()),    ],)

Batch Sending

Pythonbatch.py
result = email.send_batch(    [        {"from_": "you@x.com", "to": "a@y.com", "subject": "Hi", "text": "1"},        {"from_": "you@x.com", "to": "b@y.com", "subject": "Hi", "text": "2"},    ],    max_concurrency=10,)print(result.success_count, result.failure_count)# Results are aligned to input orderfor entry in result.results:    if not entry.success:        print(entry.index, entry.error_code, entry.error)

Templates

Pythontemplates.py
# Create a stored template — rendered server-side by SES at send timeemail.templates.create(    name="welcome",    subject="Hi {{name}}",    html="<h1>Welcome, {{name}}!</h1>",    text="Welcome, {{name}}!",)email.templates.get("welcome")                 # -> Templateemail.templates.list(page_size=20)             # -> .templates, .next_tokenemail.templates.update(    name="welcome", subject="Hey {{name}}", html="<h1>{{name}}</h1>")email.templates.delete("welcome")
Pythonsend_template.py
result = email.send_template(    template="welcome",    from_="hello@yourdomain.com",    to="user@example.com",    data={"name": "Sam"},)

Suppression

Pythonsuppression.py
# Account-level SES suppression list (bounces + complaints)email.suppression.add("bad@example.com", "COMPLAINT")email.suppression.get("bad@example.com")       # -> SuppressionEntry | Noneemail.suppression.list(reason="BOUNCE")        # -> .entries, .next_tokenemail.suppression.remove("bad@example.com")

Error Handling

Pythonerrors.py
from wraps.email import CredentialsError, SESError, ValidationErrortry:    email.send(        from_="you@x.com", to="user@y.com", subject="Hi", html="<p>Hi</p>"    )except ValidationError as err:    print("Invalid input:", err, err.field)      # caught before any AWS callexcept CredentialsError:    print("No AWS credentials found")except SESError as err:    print(err.code, err.request_id, err.retryable, err.status)

Fully Typed

Pythontyped.py
from wraps.email import SendEmailResult, WrapsEmailemail = WrapsEmail()# Explicit signatures + a PEP 561 py.typed marker mean mypy / ty / Pyright# check your calls, and editors autocomplete every argument.result: SendEmailResult = email.send(    from_="you@yourdomain.com",    to="user@example.com",    subject="Typed",    html="<p>Autocomplete + static checks</p>",)

Next Steps

TypeScript SDK

The Node/TypeScript email SDK, with React Email, inbound, and event tracking.

Email SDK Reference
Domain Verification

Verify your sending domain with DKIM so mail authenticates at Gmail and Yahoo.

Verify a Domain

Need Help?

Found a bug or have a feature request? Open an issue on GitHub.

Open an Issue