Was ist MailRudi?
MailRudi ist eine minimale, API-first Newsletter-Delivery-Plattform, die AWS Simple Email Service (SES) nutzt. Entwickelt für Entwickler, die Newsletter-Versand direkt in ihre Anwendungen integrieren möchten.
Was kann MailRudi?
- Newsletter versenden – Erstelle und versende Kampagnen per API
- Previews generieren – Vorschau vor dem Versand ohne E-Mail zu senden
- Job-Status tracken – Überwache den Versand-Status in Echtzeit
- Multi-Tenant – Isolierte API-Keys für verschiedene Nutzer
- Rate Limiting – Automatische Respektierung von SES-Limits
Wie funktioniert MailRudi?
1. API Request
Client sendet POST-Request an /v1/campaigns mit API-Key
2. Campaign Creation
API-Service erstellt Campaign in der Datenbank
3. Job Queue
Job wird in SQS-Queue (oder lokale DB-Queue) eingereiht
4. Worker Processing
Worker-Prozess verarbeitet Jobs asynchron und versendet via AWS SES
5. Status Updates
Status kann per GET /v1/jobs/{id} abgefragt werden
Platzhalter
Email-Inhalte unterstützen dynamische Platzhalter im Format {{platzhalter}}, die für jeden Empfänger individuell ersetzt werden.
- Standard-Platzhalter:
{{firstname}},{{lastname}},{{name}} - Benutzerdefinierte Platzhalter: Beliebige Felder aus
attributes(z.B.{{Alter}},{{Stadt}}) - Verwendung: In
content.subject,content.htmlundcontent.text
Recipient-Felder (alle optional außer email):
email- Pflicht - E-Mail-Adressefirstname- Optional - Vornamelastname- Optional - Nachnamename- Optional - Vollständiger Name (Fallback)attributes- Optional - Benutzerdefinierte Felder als Key-Value-Paare
API-First Approach
MailRudi wurde vollständig API-first entwickelt:
- Keine GUI erforderlich – Alles über REST API steuerbar
- Direkte Integration – Einfach in bestehende Anwendungen einbinden
- Standard HTTP – Nutzt bewährte HTTP-Methoden und Status-Codes
- JSON-basiert – Einfache, strukturierte Datenformate
- Skalierbar – API-Service und Worker getrennt skalierbar
Beispiel: Newsletter versenden
POST /v1/campaigns
X-Api-Key: your-api-key
Content-Type: application/json
{
"action": "send",
"sender": {
"from_localpart": "newsletter",
"from_name": "Team"
},
"recipients": {
"inline": [
{
"email": "user@example.com",
"firstname": "John",
"lastname": "Doe",
"attributes": {
"Alter": "25"
}
}
]
},
"content": {
"subject": "Hello {{firstname}} {{lastname}}",
"html": "<h1>Hello {{firstname}}!</h1><p>Sie sind {{Alter}} Jahre alt.</p>"
}
}
Technologie
- Backend: Node.js, Express, TypeScript
- Datenbank: PostgreSQL mit Prisma ORM
- Queue: AWS SQS (oder lokale DB-Queue für Development)
- Email Service: AWS SES
- Architektur: Microservices (API-Service + Worker-Service)
Playground
recipients.inline oder options.test_recipients)API Dokumentation
Authentifizierung: Alle Requests benötigen Header X-Api-Key
POST /v1/campaigns
Erstellt und versendet Newsletter-Kampagnen
Actions:
preview- Generiert eine Vorschau-URL ohne Versand. Keine E-Mails werden gesendet, kein Job wird erstellt. Ideal zum Testen des Designs und der Platzhalter-Ersetzung.draft- Speichert die Kampagne in der Datenbank ohne Versand. Keine E-Mails werden gesendet, kein Job wird erstellt. Nützlich zum Speichern von Entwürfen für späteren Versand.test- Test-Versand: Sendet nur an den ersten Recipient ausrecipients.inline(oder anoptions.test_recipients, falls vorhanden). Erstellt einen Job und sendet tatsächlich E-Mails. Ideal zum Testen des Versand-Prozesses mit echten E-Mails.send- Versendet an alle Recipients inrecipients.inline. Erstellt einen Job und sendet E-Mails an alle Empfänger. Dies ist der normale Produktions-Versand.
Unterschiede im Detail:
- preview vs. draft:
previewgibt eine sofortige Vorschau-URL zurück, währenddraftdie Kampagne nur speichert. Beide senden keine E-Mails. - test vs. send:
testsendet nur an einen Empfänger (den ersten), währendsendan alle Empfänger sendet. Beide erstellen Jobs und senden echte E-Mails. - Job-Status: Nur
testundsenderstellen Jobs, die überGET /v1/jobs/{jobId}abgefragt werden können.previewunddraftgeben keinejob_idzurück.
POST /v1/campaigns
X-Api-Key: your-api-key
{
"action": "send",
"sender": { "from_localpart": "newsletter", "from_name": "Team" },
"recipients": {
"inline": [{
"email": "user@example.com",
"firstname": "John",
"lastname": "Doe",
"attributes": { "Alter": "25" }
}]
},
"content": {
"subject": "Hello {{firstname}} {{lastname}}",
"html": "<h1>Hello {{firstname}}!</h1><p>Sie sind {{Alter}} Jahre alt.</p>"
}
}
GET /v1/preview/{campaignId}
Zeigt HTML-Vorschau der Kampagne im Browser (kein API-Key nötig)
GET /v1/jobs/{jobId}
Gibt Status, Fortschritt und Statistiken des Versand-Jobs zurück
GET /v1/jobs/{jobId}
X-Api-Key: your-api-key
Response: {
"status": "completed",
"counts": { "intended": 10, "sent": 10, "failed": 0 }
}
Changelog
Alle wichtigen Änderungen und Updates an MailRudi. Das Changelog ist auch Teil des Dokumentations-Downloads.
Changelog wird geladen...
LLM Integration mit Tool Schema
MailRudi kann direkt aus einem LLM (z.B. OpenAI) per Tool aufgerufen werden. Ein LLM kann Newsletter-Kampagnen erstellen und versenden, ohne dass manuelle API-Calls nötig sind.
Beispiel: TextRudi.com
TextRudi ist ein Social Media Agent für einen Sportverein, der über MailRudi seinen Mitgliedern Newsletter schreiben kann.
Wie funktioniert es?
- Der Verein gibt TextRudi eine Anweisung: "Schreibe einen Newsletter über das nächste Turnier"
- TextRudi (ein LLM) formuliert den Newsletter-Inhalt
- TextRudi ruft das Tool
send_club_newsletterauf - Das Tool sendet die Kampagne über die MailRudi API
- Die Mitglieder erhalten den Newsletter per E-Mail
Tool-Definition
Das Tool send_club_newsletter ermöglicht es dem LLM, Newsletter-Kampagnen direkt zu erstellen und zu versenden:
{
"name": "send_club_newsletter",
"description": "Erstellt und versendet eine Newsletter-Kampagne über POST /v1/campaigns.",
"parameters": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["preview", "draft", "test", "send"],
"description": "Aktion für die Kampagne"
},
"sender": {
"type": "object",
"properties": {
"from_localpart": { "type": "string" },
"from_name": { "type": "string" },
"reply_to": { "type": "string", "format": "email" }
},
"required": ["from_localpart"]
},
"recipients": {
"type": "object",
"properties": {
"inline": {
"type": "array",
"items": {
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"firstname": { "type": "string" },
"lastname": { "type": "string" },
"attributes": {
"type": "object",
"additionalProperties": true
}
},
"required": ["email"],
"additionalProperties": true
}
}
}
},
"content": {
"type": "object",
"properties": {
"subject": { "type": "string" },
"html": { "type": "string" },
"text": { "type": "string" }
},
"required": ["subject"]
},
"style": {
"type": "object",
"properties": {
"brand_color": { "type": "string" },
"logo_url": { "type": "string", "format": "uri" }
}
},
"options": {
"type": "object",
"properties": {
"test_recipients": {
"type": "array",
"items": { "type": "string", "format": "email" }
}
}
}
},
"required": ["action", "sender", "content"]
}
}
Beispiel: TextRudi erstellt einen Newsletter
User zu TextRudi: "Schreibe einen Newsletter über das nächste Fußballturnier am 15. März. Sende ihn an alle Mitglieder."
TextRudi formuliert den Newsletter und ruft das Tool auf:
{
"action": "send",
"sender": {
"from_localpart": "newsletter",
"from_name": "SV Welschbillig"
},
"recipients": {
"inline": [
{
"email": "max.mustermann@example.com",
"firstname": "Max",
"lastname": "Mustermann",
"attributes": {
"Abteilung": "Fußball",
"Mitglied_seit": "2020"
}
},
{
"email": "anna.schmidt@example.com",
"firstname": "Anna",
"lastname": "Schmidt",
"attributes": {
"Abteilung": "Fußball",
"Mitglied_seit": "2021"
}
}
]
},
"content": {
"subject": "Fußballturnier am 15. März – Jetzt anmelden!",
"html": "<h1>Hallo {{firstname}}!</h1><p>Wir freuen uns, dich über unser nächstes Fußballturnier am 15. März zu informieren.</p><p>Als Mitglied der Abteilung {{Abteilung}} seit {{Mitglied_seit}} möchten wir dich besonders herzlich einladen.</p><p>Melde dich jetzt an!</p>"
},
"style": {
"brand_color": "#0066cc",
"logo_url": "https://verein.example.com/logo.png"
}
}
System-Prompt für TextRudi
Damit TextRudi richtig funktioniert, benötigt er einen System-Prompt:
Du bist TextRudi, der Social Media Agent des Sportvereins SV Welschbillig.
Deine Aufgabe ist es, Newsletter-Inhalte zu formulieren und Versandaktionen
über das Tool "send_club_newsletter" vorzubereiten.
Wichtige Regeln:
1. Du erzeugst NIE eigene Empfänger. Du verwendest Empfängerdaten genau so,
wie sie vom System oder User bereitgestellt wurden.
2. Betreff und HTML-Inhalt formulierst du klar, präzise, geeignet für
Vereinskommunikation.
3. Du erzeugst IMMER sauberes HTML (h1, p, ul/li, strong, em).
4. Platzhalter (Placeholders):
- Verwende {{platzhalter}} Syntax für dynamische Inhalte.
- Standard-Platzhalter: {{firstname}}, {{lastname}}, {{name}}
- Custom-Platzhalter: Alle Felder aus "attributes" (z.B. {{Abteilung}}, {{Mitglied_seit}})
5. Versand-Logik:
- Wenn User nichts sagt → Standard = "preview".
- Wenn User explizit sagt „entwurf", „speichern" → action = "draft".
- Wenn User „bitte testen" → action = "test".
- Wenn User „raus damit", „an alle senden", „jetzt senden" → action = "send".
6. Nach finaler Formulierung rufst du GENAU EIN Tool auf: send_club_newsletter.
7. Antworte immer in deutscher Alltagssprache (du-Form), klar und direkt.
Dein Toolcall ist immer die letzte Ausgabe deiner Antwort.
Backend-Integration
Das Backend von TextRudi muss:
- Tool-Call vom LLM empfangen
- JSON-Validierung durchführen
- HTTP-Request an
POST /v1/campaignssenden - Response an das LLM zurückgeben
Beispiel-Implementierung (Node.js):
async function handleToolCall(toolCall) {
const { action, sender, recipients, content, style, options } = toolCall;
// Validierung
if (action === 'send' && (!recipients?.inline || recipients.inline.length === 0)) {
throw new Error('Bei action="send" ist mindestens ein inline recipient erforderlich');
}
if (!content.html && !content.source) {
throw new Error('Entweder content.html oder content.source muss vorhanden sein');
}
// API-Call
const response = await fetch('https://mailrudi.example.com/v1/campaigns', {
method: 'POST',
headers: {
'X-Api-Key': process.env.API_MAIL_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
action,
sender,
recipients,
content,
style,
options
})
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
throw new Error(`API Error: ${errorData.message || errorData.error}`);
}
return await response.json();
}
Platzhalter (Placeholders)
TextRudi kann dynamische Platzhalter verwenden, die für jeden Empfänger individuell ersetzt werden:
- Standard-Platzhalter:
{{firstname}},{{lastname}},{{name}} - Custom-Platzhalter: Alle Felder aus
attributes(z.B.{{Abteilung}},{{Mitglied_seit}})
Beispiel:
{
"content": {
"subject": "Hallo {{firstname}} {{lastname}}",
"html": "<h1>Hallo {{firstname}}!</h1><p>Du bist Mitglied der Abteilung {{Abteilung}} seit {{Mitglied_seit}}.</p>"
}
}
→ Für Max Mustermann wird {{firstname}} zu "Max", {{Abteilung}} zu "Fußball", etc.
Fehlerbehandlung
Das Backend sollte API-Fehler in benutzerfreundliche Nachrichten für das LLM umwandeln:
- 400 Bad Request: Validierungsfehler – spezifische Fehlermeldung mit Feld-Pfad
- 401 Unauthorized: API-Key ungültig oder fehlt
- 500 Internal Server Error: Server-Fehler – Bitte später erneut versuchen
Vorteile dieser Integration
- Automatisierung: Newsletter können per natürlicher Sprache erstellt werden
- Personalisierung: LLM kann Inhalte basierend auf Empfängerdaten anpassen
- Einfache Bedienung: Keine technischen API-Kenntnisse nötig
- Flexibilität: LLM kann verschiedene Newsletter-Formate und -Stile generieren
- Skalierbarkeit: MailRudi übernimmt den Versand, LLM nur die Inhaltserstellung
Konto-Einstellungen
Bitte melde dich an, um deine Kontoeinstellungen zu sehen.