23 Jahre aus Stockholm
Operator-Notizen · Aus dem Stockholm-Büro

Email API für SaaS-Produkte und event-getriebene Workflows

SaaS-Produkte sind event-getrieben. Email API ist für event-getriebene Architekturen die strukturell überlegene Wahl gegenüber SMTP-Relay.

SaaS-Produkte sind strukturell event-getrieben — User-Aktion löst Geschäfts-Logik aus, Logik triggert Nebenwirkungen. E-Mail ist eine der wichtigsten Nebenwirkungen. Email API ist für event-getriebene SaaS-Architekturen die strukturell überlegene Wahl gegenüber SMTP-Relay.

Was event-getriebene Workflows ausmacht

User klickt "Sign up"
       ↓
Anwendung erstellt Account
       ↓
Anwendung publiziert Event: "user.signed_up"
       ↓
HTTP-Response geht zurück (User sieht Dashboard)
       ↓
Parallel: mehrere Subscriber konsumieren Event
   ├─ CRM-Sync-Service
   ├─ Analytics-Service
   ├─ Onboarding-Service
   └─ E-Mail-Service (Welcome-Mail senden)

Warum Email API strukturell besser passt

Webhooks für Engagement-Events: wenn der User die Welcome-Mail öffnet, wird "email.opened" publiziert. Andere Services können reagieren.

Idempotency-Keys: wenn das gleiche Trigger-Event zweimal verarbeitet wird, würde SMTP zwei Mails versenden. Mit Idempotency-Keys erkennt die API Duplicates.

Async-by-Default: API-Aufrufe sind stateless. SMTP-Konnektion-Pools brauchen Verwaltung.

Microservice-Native: HTTP ist die natürliche Sprache der Microservices.

Architektur-Pattern

Pattern A: Direkte Integration

def handle_user_signed_up(event):
    user = User.get(event['user_id'])
    
    response = email_api.send(
        to=user.email,
        template='welcome',
        template_data={'first_name': user.first_name},
        idempotency_key=f'welcome-{user.id}-v1'
    )

Einfach. Aber: tight coupling, jeder Subscriber verwaltet API-Credentials.

Pattern B: Dedizierter Mail-Service

# Subscriber publiziert nur Event
def handle_user_signed_up(event):
    user = User.get(event['user_id'])
    event_bus.publish('mail.send_requested', {
        'template': 'welcome',
        'recipient_id': user.id,
        'template_data': {'first_name': user.first_name},
        'idempotency_key': f'welcome-{user.id}-v1'
    })

# Mail-Service handled API-Integration
class MailService:
    def handle_send_requested(event):
        recipient = User.get(event['recipient_id'])
        response = email_api.send(
            to=recipient.email,
            template=event['template'],
            template_data=event['template_data'],
            idempotency_key=event['idempotency_key']
        )

Standard für mittelständische SaaS-Architekturen.

Webhook-Verarbeitung für Choreography

@app.route('/webhooks/email-events', methods=['POST'])
def handle_email_webhook():
    if not verify_signature(request.headers, request.body):
        abort(401)
    
    events = json.loads(request.body)
    for event in events:
        event_bus.publish(f'email.{event["event"]}', {
            'recipient_email': event['recipient_email'],
            'message_id': event['message_id'],
            'custom_args': event.get('custom_args', {}),
            'timestamp': event['timestamp']
        })
    
    return '', 200

class CRMService:
    def handle_email_opened(event):
        lead_id = event['custom_args'].get('lead_id')
        if lead_id:
            Lead.get(lead_id).increment_engagement_score(2)

Idempotency-Strategien

# Welcome-Mail nach signup
idempotency_key = f"welcome-{user.id}-v1"

# Order-Confirmation
idempotency_key = f"order-confirmation-{order.id}-v2"

# Re-Engagement (wiederholt zulässig)
idempotency_key = f"re-engagement-{user.id}-{campaign_id}-{step}"

Bei Schema-Änderung Version erhöhen — sonst wird die neue Mail nicht versendet.

Custom Args für Event-Korrelation

email_api.send(
    to=user.email,
    template='welcome',
    custom_args={
        'user_id': user.id,
        'email_type': 'welcome',
        'campaign_id': 'trial-onboarding-v3',
        'sequence_step': 1,
        'tenant_id': user.tenant_id
    }
)

DACH-spezifische Aspekte

Multi-Tenant-Strukturen mit verschiedenen Sprachen (DE, AT, CH unterscheiden sich subtil). Templates pro Locale, Personalisierung mit korrekter Anrede ("Sehr geehrte Frau Müller" vs. "Liebe Anna"), AT-Begriffe (Rechnung statt Faktura).

DSGVO-konforme Webhook-Verarbeitung: Custom Args sollten keine Klartext-personenbezogenen Daten enthalten, sondern nur User-IDs.

Bei Mehr-Mandanten-SaaS mit Custom-Domain pro Tenant: jeder Tenant kann eigene From-Domain haben. Email API muss das unterstützen — bei Authorize Hosting im Pro-Tarif Standard.

API-Setup für Ihre SaaS-Architektur?

Wir besprechen Ihre Microservice-Architektur und konfigurieren API-Setup mit passenden Webhook-Endpunkten und Multi-Tenant-Support.