Transaktions-E-Mails mit Node.js, Python und cURL senden

Beispiele zum Kopieren: E-Mails per REST-API aus Node.js, Python, PHP und der Kommandozeile senden – mit Timeouts, Fehlerbehandlung und wiederverwendbarem Helper.

Transaktions-E-Mails sind die Nachrichten, die Ihr Produkt versendet, weil ein Nutzer etwas getan hat: sich registriert, ein Passwort zurückgesetzt, eine Rechnung bezahlt, eine Einladung in einen Workspace erhalten. Sie müssen schnell rausgehen, von Ihrer eigenen Domain, und ohne die Anfrage zu verlangsamen, die sie ausgelöst hat.

Dieser Artikel zeigt, wie Sie sie über die Inbixio-REST-API aus den Sprachen senden, die Teams am häufigsten verwenden, und macht aus dem einzelnen Aufruf anschließend einen kleinen Helper, den Sie in jede Codebasis übernehmen können.

Die Anfrage auf einen Blick

Jedes der folgenden Beispiele sendet dieselbe HTTP-Anfrage:

  • Methode und URL: POST https://backend.inbixio.com/api/send-email/, einschließlich des abschließenden Schrägstrichs
  • Authentifizierung: ein X-API-Key-Header mit einem Schlüssel, der die Berechtigung Send (Senden) hat
  • Body: JSON mit to_email, subject und message (HTML)

Ein erfolgreicher Aufruf gibt 200 mit {"message": "Email queued successfully."} zurück. Falls Sie noch keinen Schlüssel erstellt oder keinen SMTP-Server verbunden haben, beginnen Sie mit dem Fünf-Minuten-Schnellstart.

In allen Beispielen wird der Schlüssel aus einer Umgebungsvariablen namens INBIXIO_API_KEY gelesen. Belassen Sie ihn auf dem Server; liefern Sie ihn nie an einen Browser oder eine mobile App aus.

cURL

Praktisch, um einen Schlüssel zu testen, auf einem Server zu debuggen oder die API aus einem Shell-Skript oder Cronjob aufzurufen:

curl -sS -X POST https://backend.inbixio.com/api/send-email/ \
  -H "X-API-Key: $INBIXIO_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 15 \
  -d '{
    "to_email": "ada@example.com",
    "subject": "Your invoice is ready",
    "message": "<p>Hi Ada, your invoice for October is ready.</p>"
  }'

-sS blendet den Fortschrittsbalken aus, gibt Fehler aber weiterhin aus, und --max-time verhindert, dass ein Skript hängen bleibt, wenn das Netzwerk hängt.

Node.js

Node.js 18 und neuer bringen fetch mit, Sie brauchen also keine HTTP-Bibliothek:

// send-email.js
const INBIXIO_URL = 'https://backend.inbixio.com/api/send-email/';

export async function sendEmail({ to, subject, html }) {
  const res = await fetch(INBIXIO_URL, {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.INBIXIO_API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ to_email: to, subject, message: html }),
    signal: AbortSignal.timeout(15_000),
  });

  const body = await res.json().catch(() => ({}));
  if (!res.ok) {
    const reason = body.error || body.detail || res.statusText;
    throw new Error(`Inbixio ${res.status}: ${reason}`);
  }
  return body; // { message: 'Email queued successfully.' }
}

Und so verwenden Sie ihn in einer Express-Route:

app.post('/signup', async (req, res) => {
  const user = await createUser(req.body);
  await sendEmail({
    to: user.email,
    subject: 'Welcome aboard',
    html: `<h1>Welcome, ${escapeHtml(user.name)}!</h1><p>Your account is ready.</p>`,
  });
  res.status(201).json({ id: user.id });
});

Beachten Sie escapeHtml. Das Feld message wird als HTML dargestellt. Alles, was ein Nutzer eingegeben hat, etwa sein Name, muss daher maskiert werden, bevor Sie es in den Body einfügen.

Python

Mit requests:

# send_email.py
import os
import requests

INBIXIO_URL = "https://backend.inbixio.com/api/send-email/"

class EmailError(Exception):
    pass

def send_email(to: str, subject: str, html: str) -> dict:
    res = requests.post(
        INBIXIO_URL,
        headers={"X-API-Key": os.environ["INBIXIO_API_KEY"]},
        json={"to_email": to, "subject": subject, "message": html},
        timeout=15,
    )
    if not res.ok:
        try:
            body = res.json()
        except ValueError:
            body = {}
        reason = body.get("error") or body.get("detail") or res.reason
        raise EmailError(f"Inbixio {res.status_code}: {reason}")
    return res.json()

Mit json= wird der Content-Type-Header automatisch gesetzt. Setzen Sie immer einen timeout: requests wartet standardmäßig unbegrenzt.

Rufen Sie die Funktion in Django erst auf, nachdem die Datenbanktransaktion committet wurde. So versenden Sie nie eine E-Mail zu einem Datensatz, der zurückgerollt wurde:

from django.db import transaction

transaction.on_commit(lambda: send_email(user.email, "Welcome!", render_welcome(user)))

PHP

<?php
function send_email(string $to, string $subject, string $html): array {
    $ch = curl_init('https://backend.inbixio.com/api/send-email/');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_HTTPHEADER => [
            'X-API-Key: ' . getenv('INBIXIO_API_KEY'),
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'to_email' => $to,
            'subject'  => $subject,
            'message'  => $html,
        ]),
    ]);
    $body = json_decode(curl_exec($ch), true) ?? [];
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);

    if ($status !== 200) {
        throw new RuntimeException("Inbixio $status: " . ($body['error'] ?? $body['detail'] ?? 'request failed'));
    }
    return $body;
}

An mehrere Empfänger senden

Jede Anfrage stellt an eine einzige to_email zu. Um eine Liste von Nutzern zu benachrichtigen, senden Sie eine Anfrage pro Empfänger. Genau das wollen Sie bei Transaktions-E-Mails ohnehin: Jeder Nutzer erhält seine eigene Nachricht, niemand sieht die Adresse eines anderen, und eine fehlerhafte Adresse blockiert nicht den Rest.

Halten Sie die Parallelität moderat, damit Sie Ihren eigenen SMTP-Server nicht überlasten. Ein kleiner Pool von 5–10 parallelen Anfragen reicht für die meisten Apps völlig aus.

Nicht im Request-Pfad senden

Der API-Aufruf ist schnell, aber trotzdem ein Netzwerk-Roundtrip. Legen Sie alles, was nicht zeitkritisch ist, in einen Hintergrundjob (BullMQ, Celery, Sidekiq, eine Cloud-Queue), damit:

  • die Seite des Nutzers lädt, ohne auf die E-Mail zu warten,
  • ein vorübergehender Fehler wiederholt werden kann, ohne dass der Nutzer es merkt, und
  • eine plötzliche Spitze, etwa ein großer Import, der Tausende Willkommens-E-Mails auslöst, in gleichmäßigem Tempo abgearbeitet wird.

Bei OTP-Codes und Passwort-Resets warten Nutzer aktiv. Senden Sie hier also sofort und zeigen Sie eine freundliche Fehlermeldung, falls es fehlschlägt. Fehler und Limits der E-Mail-API behandeln erklärt, welche Fehler Sie wiederholen und welche Sie anzeigen sollten.

Checkliste vor dem Go-live

  • Der API-Schlüssel stammt aus einer Umgebungsvariablen oder einem Secrets-Manager und hat nur die Berechtigung Send.
  • Jeder vom Nutzer gelieferte Wert im HTML ist maskiert.
  • Anfragen haben einen Timeout von 10–15 Sekunden.
  • Nicht-200-Antworten werden mit dem Antwort-Body protokolliert, damit die Meldungen in error und detail nicht verloren gehen.
  • Für Ihre Absenderdomain sind SPF, DKIM und DMARC eingerichtet, damit die E-Mails im Posteingang ankommen.

Mit Inbixio versenden

Verbinden Sie Ihren eigenen SMTP-Server, erstellen Sie einen API-Schlüssel mit passenden Berechtigungen und senden Sie aus jeder Programmiersprache mit einer einzigen HTTPS-Anfrage.

Konto erstellen Preise ansehen