Senden Sie eine ausgehende E-Mail pro Empfänger von einer FORM- oder CONTACT_FORM-Integration unter Verwendung des outboundMail-SMTP dieser Integration. Der Aufruf erfolgt Server-zu-Server mit X-API-Key und der Berechtigung ``transactional.send``.
Dies ist kein zustandsloses SMTP-Relay. Mailoo erstellt einen vollständigen Versand-Lifecycle innerhalb der Integration: TransactionalSend, Message (OUTBOUND), optionaler Subscriber-Sync, Open/Click-Tracking und optionale Delivery-Webhooks. Siehe outbound-smtp{.interpreted-text role="doc"} für die Abgrenzung zur Offenlegung von SMTP-Zugangsdaten an Integratoren.
Verwandt: SMTP-Ebene --- outbound-smtp{.interpreted-text role="doc"}; Abmelde-Tokens --- website-forms{.interpreted-text role="doc"}; OpenAPI --- https://api.mailoo.app/docs/v1.
Architektur
- Auslöser, kein Relay: Ihr Backend ruft Mailoo auf; Mailoo rendert die Nachricht, speichert eine OUTBOUND-
Message(zuerstPENDING) und versendet danach über ``integration.config.outboundMail`` (dasselbe SMTP wie Dashboard-Antworten und Willkommensmails). Rendern/Persistenz und SMTP sind getrennte Schritte: ein Zustellfehler verwirft die gespeicherte Nachricht nicht (StatusFAILED;htmlContentbleibt für die Dashboard-Vorschau). - Zugangsdaten: Verwenden Sie ``X-API-Key`` mit ``transactional.send`` (oder FULL) ausschließlich von Ihrem Server oder BFF --- niemals in Browser-Bundles.
- Idempotenz: Jede Anfrage erfordert ``idempotencyKey`` (max. 256 Zeichen, eindeutig pro Integration). Erster Erfolg gibt 201 zurück; Wiederholungen geben 200 mit
idempotentReplay: truezurück. - Abonnenten: Mailoo erstellt oder aktualisiert einen
Subscriberfür die Empfänger-E-Mail. WennunsubscribedAtgesetzt ist, wird der Versand übersprungen (409, StatusSKIPPED_UNSUBSCRIBED) und es wird keineMessageerzeugt. - Zustellungsereignisse: Optionale ausgehende Webhooks (
delivered,opened,clicked,unsubscribed,bounced) mit HMAC-Signierung und Wiederholungen --- siehe Delivery-Webhooks unten.
Versand-Endpunkt
``POST {baseUrl}/api/v1/webhooks/transactional/{projectUid}/{integrationId}/send``
Header: Content-Type: application/json, X-API-Key (Berechtigung ``transactional.send`` oder FULL)
Body (JSON):
- ``idempotencyKey`` (erforderlich) --- stabiler Schlüssel für diesen logischen Versand (z. B.
daily-2026-06-20-user123) - ``to`` (erforderlich) --- Empfänger-E-Mail
- ``name`` (optional) --- Anzeigename; wird für
{{User.name}}/{{firstName}}verwendet - ``templateId`` (optional) --- Kampagnen-ID (Dashboard-Kampagne dieser Integration); verwenden Sie entweder
templateIdoder Inline-subject+text/html - ``locale`` (optional) --- Empfänger-Locale für Block-/Vorlagen-/Slot-Overlays (BLOG-Stil). Fehlt der Wert oder das Overlay, gilt kanonisches EN und wird geloggt. Wenn gesetzt, auch in
Subscriber.localegespeichert. - ``vars`` (optional) --- Objekt mit String-/Zahlen-/Boolean-Liquid-Variablen (siehe unten)
- ``subject``, ``text``, ``html`` (optional) --- Inline-Inhalt ohne
templateId; Subject und mindestenstextoderhtmlsind für Inline-Versand erforderlich
Antworten:
- 201 --- E-Mail gesendet (
status: SENT,idempotentReplay: false) - 200 --- idempotente Wiederholung eines früheren Versands
- 409 --- Empfänger abgemeldet (
status: SKIPPED_UNSUBSCRIBED) - 502 --- SMTP-Zustellung fehlgeschlagen (
TransactionalSendstatus: FAILED; OUTBOUND-Messagebleibt mitstatus: FAILEDund gerendertem Body für die Vorschau;BOUNCED-Delivery-Webhook wird aufgezeichnet) - 400 --- Validierungsfehler
- 403 --- API-Schlüssel ohne ``transactional.send``
- 404 --- Projekt/Integration nicht gefunden oder nicht FORM/CONTACT_FORM
Beispiel (Inline):
curl -X POST "https://api.mailoo.app/api/v1/webhooks/transactional/{projectUid}/{integrationId}/send" \
-H "Content-Type: application/json" \
-H "X-API-Key: mai_live_..." \
-d '{
"idempotencyKey": "daily-2026-06-20-user1",
"to": "user@example.com",
"name": "Jane Doe",
"subject": "Hi {{firstName}}",
"html": "<p>{{affirmationText}} <a href=\"{{writeUrl}}\">Write</a></p><p><a href=\"{{unsubLink}}\">Unsubscribe</a></p>",
"vars": {
"affirmationText": "You are enough.",
"writeUrl": "https://app.example.com/write"
}
}'
Beispielantwort (201):
{
"success": true,
"data": {
"sendId": "clx...",
"messageId": "clx...",
"status": "SENT",
"idempotentReplay": false
}
}
Liquid-Variablen
Vorlagen (Kampagnen-templateId oder Inline-subject/text/html) werden vor dem SMTP-Versand mit LiquidJS gerendert.
Eingebauter Kontext:
- ``User.email``, ``User.name`` --- aus
to/name - ``firstName`` --- aus
vars.firstNameoder erstes Token vonname - ``unsubLink`` --- Ein-Klick-Abmelde-URL (integrations-eigenes Signier-Secret + API-``MAILOO_PUBLIC_APP_URL``)
- ``affirmationText``, ``writeUrl``, ``streak`` --- aus
vars, wenn angegeben (üblich für Retention-/Nudge-Flows)
Zusätzliche String-/Zahlen-/Boolean-Schlüssel in vars werden in den Kontext eingefügt.
Open- und Click-Tracking
Wenn der HTML-Body nicht leer ist, fügt Mailoo ein:
- Ein 1×1-Open-Tracking-Pixel
- Umgeschriebene
https://- (und//-)Links über Click-Tracking-Redirects
Öffentliche Routen (kein API-Schlüssel):
- ``GET /api/v1/public/transactional/open/{trackingToken}`` --- gibt transparentes GIF zurück; zeichnet erstes Öffnen auf
- ``GET /api/v1/public/transactional/click/{trackingToken}?u={urlencodedTarget}`` --- zeichnet Klick auf, 302-Redirect zum Ziel (unbekanntes Token → 404, kein Redirect)
URL-Form: ein undurchsichtiges trackingToken pro TransactionalSend (keine Empfänger-E-Mail in der öffentlichen URL). Ziel ist der percent-encoded Query-Parameter u. Beim Umschreiben dekodiert Mailoo & (und verwandte Entities) in href-Werten, damit UTM nach dem Redirect erhalten bleiben.
Unique vs. raw: Nur eindeutiges Engagement wird gespeichert --- TransactionalSend.clickedAt (und CampaignRecipient.clickedAt bei Kampagne) wird beim ersten Klick gesetzt und nicht überschrieben. Es gibt keinen serverseitigen Raw-Zähler und keine Per-CTA-ID. Ein wiederholter Klick kann weiterhin den Webhook clicked mit metadata.url auslösen; UI/API- Zähler bleiben unique-first. Client- oder Proxy-Prefetch (z. B. Gmail / Outlook Safe Links) kann Unique-Zahlen erhöhen.
Open-Redirect-Hinweis: u ist nicht HMAC-signiert; ein bekanntes Token kann mit ersetztem u wiederverwendet werden. Bevorzugen Sie Links so, wie Mailoo sie umgeschrieben hat. Unbekannte Tokens leiten nicht weiter.
Setzen Sie ``MAILOO_PUBLIC_API_URL`` auf dem API-Host, damit Pixel- und Klick-URLs auf Ihre öffentliche API zeigen. Setzen Sie ``MAILOO_PUBLIC_APP_URL``, wenn Abmelde- und App-Links auf den Dashboard-Origin zeigen müssen.
Delivery-Webhooks
Wenn konfiguriert, sendet Mailoo JSON-Ereignisse per POST an Ihren Benachrichtigungs-Endpunkt.
Konfiguration (pro Integration):
integration.config.deliveryWebhook.url und .secret --- im Dashboard (Delivery webhooks auf dem Setup-Reiter der Integration) oder über PATCH …/integrations/{id} mit deliveryWebhook setzen.
Ereignisse: delivered (nach erfolgreichem SMTP), opened, clicked, unsubscribed (nach Abmeldung; weiterhin für Legacy-Verbraucher), bounced (SMTP-Fehler), consent.changed (Boolean der Listenmitgliedschaft --- Abmeldung und erneute Anmeldung / Restore / Lifecycle-Einwilligungswechsel).
Nutzlastform (JSON):
{
"event": "delivered",
"integrationId": "...",
"messageId": "...",
"idempotencyKey": "...",
"recipientEmail": "user@example.com",
"timestamp": "2026-06-20T12:00:00.000Z",
"metadata": {}
}
consent.changed ergänzt auf oberster Ebene externalUserId, marketingConsentGranted und unsubscribedAt (ISO oder null). Siehe lifecycle-events{.interpreted-text role="doc"}.
Signierung: Header ``X-Mailoo-Signature: t={unixSeconds},v1={hex}``, wobei v1 HMAC-SHA256 von "{timestamp}.{rawBody}" mit dem Webhook-Secret ist. Header ``X-Mailoo-Event`` wiederholt den Enum-Namen (z. B. DELIVERED, CONSENT_CHANGED).
Wiederholungen: Outbox-Worker (10-Sekunden-Intervall im API-Prozess) wiederholt fehlgeschlagene Zustellungen bis zu 8 Versuche mit exponentiellem Backoff (max. 5 Minuten). Setzen Sie ``DISABLE_DELIVERY_WEBHOOK_WORKER=1`` nur für Tests oder wenn Sie einen separaten Prozessor betreiben.
Voraussetzungen
- FORM- oder CONTACT_FORM-Integration mit ACTIVE-Status
- ``outboundMail`` konfiguriert (Dashboard → Connection & settings → Outbound email)
- ``MAIL_SMTP_ENCRYPTION_KEY`` auf der API, wenn SMTP-Passwort oder Abmelde-Secret verschlüsselt gespeichert wird
- RESTRICTED- oder FULL-API-Schlüssel mit ``transactional.send``
Abmelde-Signierung
Jede FORM-/CONTACT_FORM-Integration speichert ein verschlüsseltes Abmelde-HMAC-Secret in integration.config. Mailoo generiert {{unsubLink}} automatisch in Transaktionsvorlagen (Bestätigungsseite unter ``MAILOO_PUBLIC_APP_URL``). Eigentümer können das Secret im Dashboard rotieren (macht Links in bereits gesendeten E-Mails ungültig). Externe Backends, die eigene Links signieren, benötigen den rotierten Klartext einmalig, oder sollten stattdessen ``POST /api/v1/webhooks/unsubscribe`` mit einem API-Schlüssel aufrufen.
RFC 8058 List-Unsubscribe: Transaktions-, Kampagnen- und Lifecycle-Versand über sendTransactionalEmail setzt zusätzlich List-Unsubscribe und List-Unsubscribe-Post: List-Unsubscribe=One-Click auf ``POST /api/v1/public/unsubscribe/one-click?email=&integrationId=&token=`` (Basis ``MAILOO_PUBLIC_API_URL``). Anbieter senden form-urlencoded List-Unsubscribe=One-Click (oder leeren Body); der Endpunkt ist idempotent und antwortet mit Plain-Text OK. Dashboard-Antworten (Create reply) und Offer-Generator-Mail setzen diese Header nicht.