Taifa MailTaifa Mail Docs
SDKs

Python SDK

Install and use the official taifa-mail Python client to send email, manage domains, mailboxes, contacts, suppressions, templates, and webhooks.

The official Python client for Taifa Mail wraps the Emails API and every other Core resource in a small, typed surface. It is published as taifa-mail on PyPI, and the source lives in the taifa-mail-sdks repository.

Installation

pip install taifa-mail

The package has zero runtime dependencies (it uses only the Python standard library) and supports Python 3.8 and above.

Client setup

Create a client with your API key. Keys start with tfm_k_ and are passed as a bearer token on every request.

from taifa_mail import TaifaMail
 
taifamail = TaifaMail(api_key="tfm_k_your_api_key")

The constructor accepts the following options:

OptionTypeDefaultDescription
api_keystrrequiredYour Taifa Mail API key. Must start with tfm_k_.
base_urlstrhttps://govconnect.keOverride the API host (for staging or self-hosted setups).
max_retriesint3Maximum attempts per request. Retries apply to 429 and 5xx responses.
timeoutfloat30.0Per-request timeout in seconds.
taifamail = TaifaMail(
    api_key="tfm_k_your_api_key",
    base_url="https://govconnect.ke",
    max_retries=5,
    timeout=60.0,
)

Keep your API key on the server. Never embed it in client-side code or commit it to source control. Load it from an environment variable instead.

The client exposes seven resource groups: emails, domains, mailboxes, contacts, suppressions, templates, and webhooks.

Send an email

Pass a message dictionary to taifamail.emails.send. The from, to, cc, and bcc fields accept a bare email string, a {"email", "name"} dict, or a list of either.

from taifa_mail import TaifaMail
 
taifamail = TaifaMail(api_key="tfm_k_your_api_key")
 
result = taifamail.emails.send({
    "from": "info@health.go.ke",
    "to": "citizen@example.com",
    "subject": "Service request received",
    "html": "<p>Your service request has been received.</p>",
})
 
print(result["id"], result["status"])

You expose a clean from key. The SDK maps it to the API's wire field from_ for you, so you never write from_ yourself.

A richer send can set names, multiple recipients, a reply-to, tags, and a schedule. The send_at field accepts a datetime or an ISO 8601 string.

from datetime import datetime, timedelta
 
result = taifamail.emails.send({
    "from": {"email": "info@health.go.ke", "name": "Ministry of Health"},
    "to": [
        {"email": "jane@example.com", "name": "Jane Doe"},
        "team@example.com",
    ],
    "cc": "manager@example.com",
    "subject": "Welcome aboard",
    "html": "<h1>Welcome!</h1>",
    "text": "Welcome!",
    "reply_to": "support@yourdomain.com",
    "headers": {"X-Campaign": "onboarding"},
    "tags": ["onboarding"],
    "send_at": datetime.utcnow() + timedelta(hours=1),
})

The send returns a result dict shaped like this:

{
    "id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
    "status": "queued",
    "message_id": "<uuid@govconnect.ke>",
    "rejection_reason": None,
}

Emails

The taifamail.emails resource covers sending, lookups, search, scheduling, and inspection.

# Send a batch (one dict per email, up to 500 per request).
taifamail.emails.send_batch([
    {"from": "info@health.go.ke", "to": "a@example.com", "subject": "Hi", "html": "<p>1</p>"},
    {"from": "info@health.go.ke", "to": "b@example.com", "subject": "Hi", "html": "<p>2</p>"},
])
 
# Dry-run a send without delivering it.
check = taifamail.emails.validate({
    "from": "info@health.go.ke",
    "to": "citizen@example.com",
    "subject": "Test",
    "html": "<p>Test</p>",
})
print(check["valid"], check["can_send"])
 
# List recent emails (zero-based paging).
emails = taifamail.emails.list(status="delivered", page=0, limit=20)
 
# Fetch one email with its bodies and events.
email = taifamail.emails.get("a1b2c3d4-5678-90ab-cdef-1234567890ab")
 
# Event timeline for one email.
events = taifamail.emails.events("a1b2c3d4-5678-90ab-cdef-1234567890ab")
 
# Re-send a bounced, rejected, or failed email.
taifamail.emails.retry("a1b2c3d4-5678-90ab-cdef-1234567890ab")
 
# Search with inline tokens (to:, from:, status:, domain:, tag:).
hits = taifamail.emails.search(q="welcome status:delivered")
 
# Schedule management.
scheduled = taifamail.emails.list_scheduled()
taifamail.emails.cancel_scheduled("b2c3d4e5-6789-01bc-defg-2345678901bc")
taifamail.emails.send_scheduled_now("b2c3d4e5-6789-01bc-defg-2345678901bc")
 
# Poll for status changes since a timestamp (datetime or ISO string).
updated = taifamail.emails.updates("2026-04-06T10:00:00Z")
 
# Saved searches.
saved = taifamail.emails.get_saved_searches()
taifamail.emails.set_saved_searches(saved)

Domains

The taifamail.domains resource registers, verifies, inspects, and transfers sending domains.

# Register a domain. The response includes the DNS records to publish.
domain = taifamail.domains.create("yourdomain.com")
 
# List and fetch.
domains = taifamail.domains.list()
domain = taifamail.domains.get(domain["id"])
 
# Re-check DNS and verify.
taifamail.domains.verify(domain["id"])
 
# Live DNS checks and diagnostics.
health = taifamail.domains.health(domain["id"])
diagnosis = taifamail.domains.diagnose(domain["id"])
mx = taifamail.domains.mx_status(domain["id"])
records = taifamail.domains.published_records(domain["id"])
 
# Rotate the DKIM key.
taifamail.domains.rotate_dkim(domain["id"])
 
# Transfer to another Taifa Mail account.
taifamail.domains.transfer(domain["id"], target_email="new-owner@example.com", note="handover")
 
# Availability checks.
taifamail.domains.check_availability("newdomain.com")
taifamail.domains.check("yourdomain.com")
 
# Delete.
taifamail.domains.delete(domain["id"])

Contacts

The taifamail.contacts resource manages subscriber lists, their contacts, CSV imports, and templated bulk sends.

# Lists.
lists = taifamail.contacts.list_lists()
contact_list = taifamail.contacts.create_list(name="Newsletter", description="Monthly updates")
detail = taifamail.contacts.get_list(contact_list["id"], page=0, limit=50)
taifamail.contacts.update_list(contact_list["id"], name="Monthly Newsletter")
taifamail.contacts.delete_list(contact_list["id"])
 
# Contacts.
contact = taifamail.contacts.add_contact(
    contact_list["id"],
    email="stakeholder@example.com",
    name="Sam",
    metadata={"department": "finance"},
)
taifamail.contacts.remove_contact(contact_list["id"], contact["id"])
 
# Import from a CSV file (multipart upload; email column auto-detected).
with open("contacts.csv", "rb") as f:
    taifamail.contacts.upload_csv(contact_list["id"], f.read(), filename="contacts.csv")
 
# Send a templated email to every contact in the list.
# Subject/html/text may use {{email}}, {{name}}, and {{metadata_key}} placeholders.
taifamail.contacts.bulk_send(
    contact_list["id"],
    mailbox_id="mailbox-uuid",
    subject="Hello {{name}}",
    html="<p>Hi {{name}}</p>",
)

Suppressions

The taifamail.suppressions resource manages the do-not-send list. The list returns a paginated envelope of {items, total, page, limit}.

# Paginated list with optional search.
page = taifamail.suppressions.list(page=0, limit=50, search="example.com")
for item in page["items"]:
    print(item)
 
# Suppress a single address.
taifamail.suppressions.add(email="bounced@example.com", reason="manual")
 
# Bulk import from a file (one email per line).
with open("suppressions.txt", "rb") as f:
    taifamail.suppressions.bulk_upload(f.read(), filename="suppressions.txt")
 
# Remove a suppression.
taifamail.suppressions.remove("suppression-uuid")

Templates

The taifamail.templates resource manages reusable email templates.

templates = taifamail.templates.list()
 
template = taifamail.templates.create(
    name="Receipt",
    subject="Service request received",
    html="<p>Thanks, {{name}}.</p>",
)
 
taifamail.templates.get(template["id"])
taifamail.templates.update(template["id"], subject="Your updated receipt")
taifamail.templates.duplicate(template["id"])
taifamail.templates.delete(template["id"])

The html and text arguments map to the wire fields html_body and text_body. Template variables are derived server-side from {{name}} placeholders, so you do not pass them.

Webhooks

The taifamail.webhooks resource manages event subscriptions and inspects deliveries.

# Create a webhook. The signing secret is generated and returned.
hook = taifamail.webhooks.create(
    url="https://your-app.example.com/hooks/taifamail",
    events=["email.delivered", "email.bounced"],
)
 
# List and update.
hooks = taifamail.webhooks.list()
taifamail.webhooks.update(hook["id"], is_active=False)
 
# Send a sample email.delivered delivery to test the endpoint.
taifamail.webhooks.test(hook["id"])
 
# Inspect delivery attempts (paginated envelope).
deliveries = taifamail.webhooks.list_deliveries(hook["id"], page=0, limit=20)
delivery = taifamail.webhooks.get_delivery(hook["id"], "delivery-uuid")
 
# Delete.
taifamail.webhooks.delete(hook["id"])

Error handling

Any non-2xx response, or a transport failure that survives every retry, raises TaifaMailError. Inspect its attributes to branch on specific failures.

AttributeTypeDescription
statusintHTTP status code. 0 indicates a transport or network failure.
codestr or NoneMachine-readable error code from the API body, when present.
messagestrHuman-readable message (the exception's string value).
detailanyThe raw parsed response body, for debugging.
from taifa_mail import TaifaMail, TaifaMailError
 
taifamail = TaifaMail(api_key="tfm_k_your_api_key")
 
try:
    taifamail.emails.send({
        "from": "info@health.go.ke",
        "to": "citizen@example.com",
        "subject": "Service request received",
        "html": "<p>Your service request has been received.</p>",
    })
except TaifaMailError as e:
    print(f"status={e.status} code={e.code} message={e}")
    if e.status == 422:
        # Sender not registered, or domain not verified.
        ...

Configuration

The client tunes its network behaviour through three constructor options.

  • Retries. Requests that return 429 (rate limited) or any 5xx are retried automatically, up to max_retries attempts (default 3). Backoff is exponential, and a Retry-After header is honoured when present. Uploads are not retried, because they are not idempotent.
  • Timeout. Each request is bounded by timeout seconds (default 30.0). A timeout that exhausts all retries raises TaifaMailError with status set to 0.
  • Base URL. Point base_url at a staging or self-hosted host. It defaults to https://govconnect.ke and trailing slashes are trimmed.
taifamail = TaifaMail(
    api_key="tfm_k_your_api_key",
    base_url="https://staging.govconnect.ke",
    max_retries=5,
    timeout=45.0,
)

Next steps

  • SDK overview - compare the Python client with the SDKs for other languages.
  • Emails API reference - the underlying REST endpoints, request bodies, and response shapes.

On this page