Wysyłka e-maili transakcyjnych z Node.js, Pythona i cURL przez API

Gotowe przykłady wysyłki e-maili przez REST API z Node.js, Pythona, PHP i wiersza poleceń, z timeoutami, obsługą błędów i funkcją pomocniczą.

E-maile transakcyjne to wiadomości, które Twój produkt wysyła, bo użytkownik coś zrobił: zarejestrował się, zresetował hasło, opłacił fakturę, dostał zaproszenie do przestrzeni roboczej. Muszą wychodzić szybko, z Twojej własnej domeny i bez spowalniania żądania, które je wywołało.

Ten artykuł pokazuje, jak wysyłać je przez REST API Inbixio z języków, których zespoły używają najczęściej, a potem zamienia jednorazowe wywołanie w niewielką funkcję pomocniczą, którą wstawisz do dowolnego projektu.

Żądanie w jednym miejscu

Każdy z poniższych przykładów wysyła to samo żądanie HTTP:

  • Metoda i URL: POST https://backend.inbixio.com/api/send-email/, razem z końcowym ukośnikiem
  • Uwierzytelnianie: nagłówek X-API-Key z kluczem mającym uprawnienie Send (wysyłanie)
  • Treść: JSON z polami to_email, subject i message (HTML)

Udane wywołanie zwraca 200 z {"message": "Email queued successfully."}. Jeśli nie masz jeszcze klucza ani podłączonego serwera SMTP, zacznij od pięciominutowego szybkiego startu.

We wszystkich przykładach klucz jest odczytywany ze zmiennej środowiskowej INBIXIO_API_KEY. Trzymaj go na serwerze; nigdy nie wysyłaj go do przeglądarki ani aplikacji mobilnej.

cURL

Przydaje się do testowania klucza, debugowania z serwera albo wywołań ze skryptu powłoki czy zadania cron:

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 ukrywa pasek postępu, ale nadal wypisuje błędy, a --max-time nie pozwala skryptowi zawisnąć, gdy zawiedzie sieć.

Node.js

Node.js 18 i nowsze mają wbudowany fetch, więc nie potrzebujesz biblioteki HTTP:

// 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.' }
}

A tak wygląda użycie w trasie Express:

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 });
});

Zwróć uwagę na escapeHtml. Pole message jest renderowane jako HTML, więc wszystko, co wpisał użytkownik, np. jego imię, musi zostać zabezpieczone (escaped), zanim trafi do treści.

Python

Z biblioteką 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()

Przekazanie json= ustawia nagłówek Content-Type za Ciebie. Zawsze ustawiaj timeout: domyślnie requests czeka w nieskończoność.

W Django wywołuj wysyłkę dopiero po zatwierdzeniu transakcji w bazie, aby nigdy nie wysłać e-maila o rekordzie, który został wycofany:

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;
}

Wysyłka do kilku osób

Każde żądanie dostarcza wiadomość do jednego to_email. Aby powiadomić listę użytkowników, wyślij jedno żądanie na odbiorcę. W przypadku e-maili transakcyjnych i tak tego właśnie chcesz: każdy użytkownik dostaje własną wiadomość, nikt nie widzi cudzego adresu, a jeden błędny adres nie blokuje pozostałych.

Utrzymuj umiarkowaną współbieżność, aby nie przeciążyć własnego serwera SMTP. Pula 5–10 równoległych żądań w zupełności wystarcza większości aplikacji.

Nie wysyłaj w trakcie obsługi żądania

Wywołanie API jest szybkie, ale to wciąż przejście przez sieć tam i z powrotem. Wszystko, co nie jest krytyczne czasowo, przenieś do zadania w tle (BullMQ, Celery, Sidekiq, kolejka w chmurze), dzięki czemu:

  • strona użytkownika ładuje się bez czekania na e-mail,
  • chwilowy błąd można ponowić bez wiedzy użytkownika,
  • nagły skok, np. duży import wywołujący tysiące e-maili powitalnych, jest obsługiwany w równym tempie.

Przy kodach OTP i resetowaniu hasła użytkownicy aktywnie czekają, więc wysyłaj od razu i pokaż przyjazny komunikat, jeśli się nie uda. Artykuł obsługa błędów i limitów API e-mail wyjaśnia, które błędy ponawiać, a które pokazywać użytkownikowi.

Checklista przed wdrożeniem

  • Klucz API pochodzi ze zmiennej środowiskowej lub menedżera sekretów i ma wyłącznie uprawnienie Send (wysyłanie).
  • Każda wartość podana przez użytkownika w HTML jest zabezpieczona (escaped).
  • Żądania mają timeout 10–15 sekund.
  • Odpowiedzi inne niż 200 są logowane razem z treścią odpowiedzi, więc komunikaty error i detail nie giną.
  • Twoja domena nadawcza ma skonfigurowane SPF, DKIM i DMARC, więc e-maile trafiają do skrzynki odbiorczej.

Wyślij to przez Inbixio

Podłącz własny serwer SMTP, utwórz klucz API z ograniczonymi uprawnieniami i wysyłaj z dowolnego języka jednym żądaniem HTTPS.

Załóż konto Zobacz cennik