MailLandDocs
E-Mail-Versand

REST API

E-Mails über die Mail.Land REST API versenden mit vollständiger Anfrage- und Antwortreferenz

Senden Sie transaktionale E-Mails über POST /v1/transactional/send. Dies ist die primäre Methode zum Versenden von E-Mails über MailLand.

Authentifizierung

Fügen Sie Ihren API-Schlüssel im X-API-Key-Header ein:

X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD...

Die Zugangsdaten müssen den Api-Kanal aktiviert haben und Sending-Produktzugangsdaten sein.

Eine einzelne E-Mail senden

POST https://api.mail.land/v1/transactional/send
Send an email
curl -X POST https://api.mail.land/v1/transactional/send \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": {"email": "[email protected]"},
    "to": [{"email": "[email protected]"}],
    "subject": "Hello from MailLand",
    "htmlContent": "<p>Your email content here</p>"
  }'

Anfragekörper

Full request example
{
  "from": {
    "email": "[email protected]",
    "name": "Your App"
  },
  "to": [
    {
      "email": "[email protected]",
      "name": "Jane Doe",
      "variables": {
        "firstName": "Jane",
        "orderNumber": "ORD-12345"
      }
    }
  ],
  "subject": "Your order #{{orderNumber}} has shipped",
  "htmlContent": "<h1>Hi {{firstName}},</h1><p>Your order #{{orderNumber}} is on its way.</p>",
  "textContent": "Hi {{firstName}}, Your order #{{orderNumber}} is on its way.",
  "replyTo": "[email protected]",
  "tags": ["order-shipped", "transactional"],
  "customHeaders": {
    "X-Entity-Ref-ID": "order-12345"
  },
  "tracking": {
    "opens": true,
    "clicks": true
  },
  "priority": "High",
  "idempotencyKey": "order-12345-shipped-v1"
}

Feldreferenz

FeldTypErforderlichBeschreibung
fromobjectJaAbsenderadresse
from.emailstringJaAbsender-E-Mail (muss auf einer verifizierten Domain sein)
from.namestringNeinAnzeigename
toarrayJaEmpfängerliste
to[].emailstringJaE-Mail-Adresse des Empfängers
to[].namestringNeinAnzeigename des Empfängers
to[].variablesobjectNeinEmpfängerspezifische Template-Variablen
subjectstringBedingtErforderlich, sofern kein Template verwendet wird
templateIdstringNeinGespeichertes Template statt Inline-Inhalt verwenden
htmlContentstringBedingtHTML-Körper (htmlContent oder textContent erforderlich, wenn kein templateId vorhanden ist)
textContentstringNeinNur-Text-Fallback
variablesobjectNeinGlobale Template-Variablen. Werden unabhängig davon angewendet, ob templateId gesetzt ist; bei überschneidenden Schlüsseln gewinnt der empfängereigene Wert aus to[].variables
replyTostringNeinAntwort-E-Mail-Adresse
tagsarrayNeinTags zum Filtern in Analysen und Webhooks
customHeadersobjectNeinBenutzerdefinierte E-Mail-Header (Schlüssel-Wert-Paare)
trackingobjectNeinTracking-Einstellungen überschreiben
tracking.opensbooleanNeinÖffnungs-Tracking für diese Nachricht anfordern (Standard: aus in beiden Streams). Gilt nur, solange die sendende Domain nicht gewählt hat; die eigene Wahl der Domain pro Stream auf der Seite Domains hat Vorrang. false schaltet es immer aus
tracking.clicksbooleanNeinKlick-Tracking aktivieren (Standard: false für Transaktions-Streams, sofern Ihr Konto es nicht aktiviert, true für Marketing)
ipPoolIdstringNeinReserviert für dedizierte IP-Pools. Noch nicht verfügbar – wird es gesendet, kommt 400 IP_POOL_NOT_FOUND zurück. Weglassen.
scheduledAtdatetimeNeinFür zukünftige Zustellung planen (UTC), bis zu 7 Tage im Voraus
idempotencyKeystringNeinDoppelten Versand für denselben Schlüssel verhindern. Bevorzugt den Header X-Idempotency-Key
attachmentsarrayNeinDateianhänge (max. 10, max. 10 MB dekodiert insgesamt)
priorityenumNeinLow, Normal (Standard), High oder Critical

Anhänge

Jeder Anhang erfordert drei Felder:

Attachment format
{
  "attachments": [
    {
      "fileName": "invoice.pdf",
      "contentType": "application/pdf",
      "content": "JVBERi0xLjQKJ...",
      "inline": false
    },
    {
      "fileName": "logo.png",
      "contentType": "image/png",
      "content": "iVBORw0KGgoAAAA...",
      "contentId": "company-logo",
      "inline": true
    }
  ]
}
FeldTypErforderlichBeschreibung
fileNamestringJaDateiname mit Erweiterung
contentTypestringJaMIME-Typ (z.B. application/pdf)
contentstringJaStandard-Base64-kodierter Dateiinhalt – mit Padding, ohne Leerzeichen oder Zeilenumbrüche
contentIdstringNeinInhalts-ID für Inline-Anhänge (im HTML als cid:company-logo referenzieren)
inlinebooleanNeinOb es sich um ein Inline-Bild handelt (Standard: false)

Höchstens 10 Anhänge pro E-Mail und höchstens 10 MB dekodiert (nach Base64) insgesamt. Ein fehlendes contentType, nicht-kanonisches Base64, oder eine Überschreitung der Anzahl oder der Größe ergibt 400 VALIDATION_FAILED. POST /v1/transactional/send-batch hat kein attachments-Feld.

Antwort

Status: 202 Accepted

Success response
{
  "success": true,
  "jobId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "messageIds": ["t_a1b2c3d4e5f6..."],
  "acceptedRecipients": ["[email protected]"],
  "rejectedRecipients": [],
  "queuedAt": "2026-02-17T10:30:00Z",
  "isSandboxed": false
}
FeldTypBeschreibung
successbooleanOb die Anfrage akzeptiert wurde
jobIdstringEindeutige Job-ID für die Nachverfolgung
messageIdsarrayNachrichten-IDs pro Empfänger (t_-Präfix für transaktional, m_ für Marketing)
acceptedRecipientsarrayZur Zustellung akzeptierte E-Mail-Adressen
rejectedRecipientsarrayAbgelehnte Empfänger
queuedAtdatetimeZeitpunkt der Einreihung in die Warteschlange
isSandboxedbooleantrue, wenn die Anfrage ein Sandbox-Credential verwendet hat (siehe Sandbox-Modus)
sandboxReasonstringcredential_sandbox_mode (protokolliert, nicht zugestellt) oder all_recipients_not_in_sandbox_allowlist (kein Empfänger auf der Zulassungsliste); sonst nicht vorhanden

Abgelehnte Empfänger

Empfänger können aus verschiedenen Gründen abgelehnt werden:

Response with rejected recipients
{
  "success": true,
  "jobId": "...",
  "acceptedRecipients": ["[email protected]"],
  "rejectedRecipients": [
    {
      "email": "[email protected]",
      "reason": "Suppressed"
    },
    {
      "email": "[email protected]",
      "reason": "domain_typo",
      "suggestion": "[email protected]"
    }
  ]
}

Häufige Ablehnungsgründe:

  • Suppressed: Adresse auf der Unterdrückungsliste (frühere Bounces/Beschwerden)
  • disposable_domain: Wegwerf-E-Mail-Anbieter erkannt
  • domain_typo: wahrscheinlicher Tippfehler in der Domain (enthält suggestion-Feld)

Vorabprüfung

Überprüfen Sie Ihre Sendefähigkeit, bevor Sie die eigentliche Anfrage stellen:

GET https://api.mail.land/v1/transactional/send/check?recipientCount=1
Pre-flight check
curl https://api.mail.land/v1/transactional/send/check?recipientCount=1 \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."
Pre-flight response
{
  "canSend": true,
  "rateLimitStatus": {
    "currentSecond": 2,
    "limitPerSecond": 50,
    "currentHour": 450,
    "limitPerHour": 5000,
    "currentDay": 8200,
    "limitPerDay": 50000
  },
  "monthlyUsed": 125000,
  "monthlyLimit": 500000
}

Wenn canSend false ist, erklärt das reason-Feld den Grund (Ratenlimit, Kontingent, Kontostatus usw.).

Idempotenz

Ein Wiederholungsversuch nach einem Netzwerk-Timeout darf die E-Mail nicht zweimal zustellen. Mit einem Idempotenz-Schlüssel gibt ein erneuter Aufruf die ursprüngliche Antwort zurück, statt erneut zu senden.

Schlüssel als Header senden (empfohlen):

X-Idempotency-Key: order-12345-shipped-v1

Alternativ das Body-Feld idempotencyKey. Beide werden akzeptiert; beide gemeinsam nur bei identischen Werten.

Regeln

  • Geltungsbereich. X-Idempotency-Key wird ausschließlich von POST /v1/transactional/send und POST /v1/transactional/send-batch gelesen.
  • Format. 1–255 Zeichen: Buchstaben, Ziffern, _ und -. Schlüssel werden exakt verglichen: ohne Trimmen, ohne Groß-/Kleinschreibungsangleich.
  • Zeitfenster. Ein Schlüssel bleibt 24 Stunden wiederholbar. Danach gilt er als neue Anfrage.
  • Wiedergabe. Ein Wiederholungsversuch im Zeitfenster liefert die ursprüngliche Antwort unverändert (gleiche IDs, gleicher Status) und sendet nichts.
  • Eindeutigkeit. Schlüssel sind pro Konto eindeutig. Ein Schlüssel für einen anderen Endpunkt wird mit 409 abgelehnt; pro Vorgang einen eigenen Schlüssel verwenden.

Korrelations-ID

Verfolgen Sie Anfragen systemübergreifend durch Angabe einer Korrelations-ID:

X-Correlation-ID: my-app-order-12345

oder

X-Request-ID: my-app-order-12345

Die Korrelations-ID erscheint in Logs und im Activity Stream.

Ratenlimit-Header

Erfolgreiche Antworten enthalten Pro-IP-Ratenlimit-Informationen:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 295

Bei Ratenlimitierung (429) durch die Sende-Policy-Engine gibt ein Retry-After-Header die Wartezeit in Sekunden an:

Retry-After: 30

Eine transaktionale Anfrage darf bis zu 5 Empfänger nennen, und jeder zählt gegen Ihre Ratenlimits. Ist das mehr als Ihr Pro-Sekunde-Kontingent (zum Beispiel 1 pro Sekunde bei einem Plan mit 60 pro Minute), wird die Anfrage angenommen, wenn in derselben Sekunde keine andere Anfrage angenommen wurde; andernfalls erhält sie 429 mit Retry-After: 1 und ist bei einem erneuten Versuch in der nächsten Sekunde erfolgreich, im Rahmen Ihres Pro-Minute-Limits. Siehe Ratenbegrenzungen.

Ein IP-Level-429 enthält zusätzlich X-RateLimit-Reset als ISO-8601-UTC-Zeit.

Fehlerantworten

Alle Fehler geben einen JSON-Körper zurück:

Error response format
{
  "code": "ERROR_CODE",
  "message": "Human-readable description",
  "timestamp": "2026-02-17T10:30:00Z",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Fehlercodes

StatusCodeBeschreibung
400INVALID_REQUESTFehlerhaft formatierter Anfragekörper, oder scheduledAt mehr als 7 Tage im Voraus ("Cannot schedule email more than 7 days in advance")
400VALIDATION_FAILEDKein htmlContent, textContent oder templateId; oder ein Anhang besteht die Validierung nicht (siehe die Anhangsgrenzen oben). Antwort enthält errors[] mit Feld und verletzter Regel. (Zuvor: MISSING_CONTENT: veraltet seit 2026-04.)
413CONTENT_TOO_LARGE"The message is too large to send: it would exceed the platform's message-size limit. Reduce the HTML, text, attachments or recipients.": Die Nachricht überschreitet das Größenlimit der Plattform. POST /v1/transactional/send und send-batch liefern 413; nichts wird eingereiht. Verkleinern Sie die Nachricht. Beim CSV-Import ist es 400
400transactional_recipient_limit_exceededMehr als 5 Empfänger bei einem transaktionalen Zugangsdatensatz (1.000 bei Marketing)
400TEMPLATE_NOT_FOUNDtemplateId benennt kein aktives Template dieses Kontos (wird auch für ein gelöschtes Template zurückgegeben)
400IP_POOL_NOT_FOUNDipPoolId benennt keinen diesem Konto zugewiesenen Pool (derzeit immer, da noch kein Pool zugewiesen werden kann, siehe Feldreferenz oben)
422TEMPLATE_VARIABLE_MISSINGDer Inhalt enthält einen {{placeholder}} ohne Wert; details.missingVariables nennt die Namen. Nichts wird eingereiht
400IDEMPOTENCY_KEY_INVALIDSchlüssel ist leer, ungültig, länger als 255 Zeichen, oder der Header wurde mehrfach gesendet
400IDEMPOTENCY_KEY_CONFLICTHeader und Body-Feld idempotencyKey wurden beide gesendet und unterscheiden sich
400SEND_FAILEDAlle Empfänger sind unterdrückt (Meldung: „All recipients are suppressed")
409IDEMPOTENCY_KEY_REUSEDSchlüssel wird bereits für einen anderen Endpunkt dieses Kontos verwendet
401UNAUTHORIZEDX-API-Key fehlt, ist ungültig, abgelaufen oder widerrufen
403ACCOUNT_SUSPENDEDKonto wurde gesperrt
403ACCOUNT_DISABLEDKonto wurde deaktiviert
403ACCOUNT_NOT_ACTIVEKonto ist nicht aktiv
403NO_ACTIVE_SUBSCRIPTIONKonto hat kein aktives Abonnement
403SUBSCRIPTION_EXPIREDAbonnement des Kontos ist abgelaufen
403IP_NOT_ALLOWEDAnfrage-IP nicht in der Zulassungsliste der Zugangsdaten
403SENDING_BLOCKEDVersand für dieses Konto ist gesperrt
403ACTION_REQUIREDVor dem Versand ist eine Kontoaktion erforderlich
403TENANT_APPROVAL_REQUIREDDas Konto wartet vor dem Versand auf eine Prüfung. Wiederholen hilft nicht; wenden Sie sich an den Support
422DOMAIN_NOT_VERIFIEDAbsender-Domain nicht verifiziert
422DOMAIN_NOT_ALLOWED_FOR_CREDENTIALDomain nicht im Gültigkeitsbereich der Zugangsdaten
422DOMAIN_NOT_FOUNDAbsender-Domain nicht gefunden
429RATE_LIMIT_EXCEEDEDRatenlimit überschritten
429QUOTA_EXCEEDEDMonatliches Kontingent erschöpft
429WARMUP_LIMIT_EXCEEDEDDas tägliche Anlauflimit Ihres Kontos für neuen Versand ist erreicht; Zurücksetzung um 00:00 UTC
429DAILY_LIMIT_EXCEEDEDTageslimit erreicht
429WARMUP_POOL_BUDGET_EXCEEDED"Sending is temporarily paused for today. It resumes automatically at 00:00 UTC; no action is needed." / "Sending is briefly delayed. Please retry in a few minutes."
429WARMUP_POOL_EMPTY"Sending is temporarily paused. It resumes automatically; no action is needed."
429PLATFORM_DAILY_CAPACITY_REACHED"Platform sending capacity for today is fully booked. This is a temporary platform-wide limit, not your account quota, and it resets at midnight UTC."
429PLATFORM_CAPACITY_PROTECTION"New sending is briefly paused to protect platform capacity. Mail already accepted is not affected. This is transient and clears on its own; retry shortly."
429PROBATION_CAP_REACHED"New accounts can send to N recipients per rolling 24 hours until our team has reviewed the account. Capacity returns as earlier sends age out of the 24-hour window; contact support to request a review."
429PAYMENT_DAILY_LIMIT_EXCEEDEDTageslimit bei überfälligem Konto erreicht
429MARKETING_PAUSEDMarketing-Versand für dieses Konto pausiert
429SENDING_BLOCKEDVerifizierungsstufe des Kontos erlaubt diesen Versand nicht (Sandbox-/Verifizierungsablehnung; eine harte Versandsperre liefert stattdessen 403, siehe oben)
503SERVICE_UNAVAILABLEE-Mail-Dienst vorübergehend nicht verfügbar

Transaktionales Empfängerlimit

Transaktionale Zugangsdaten erzwingen maximal 5 Empfänger pro Sendeanfrage. Dies verhindert den Missbrauch transaktionaler Streams für den Massenversand. Falls Sie mehr Empfänger benötigen, verwenden Sie Marketing-Zugangsdaten mit Batch-Versand.

Templates verwenden

Verwenden Sie anstelle von Inline-HTML ein gespeichertes Template:

Send with a template
{
  "from": { "email": "[email protected]" },
  "to": [
    {
      "email": "[email protected]",
      "variables": {
        "firstName": "Jane",
        "resetLink": "https://app.example.com/reset?token=abc123"
      }
    }
  ],
  "templateId": "t8x2m4q9zk7wpr",
  "variables": {
    "companyName": "Acme Inc"
  }
}

Der subject stammt aus dem Template. Bei übereinstimmenden Schlüsseln überschreibt der empfängereigene Wert aus to[].variables die globalen variables, dieselbe Regel wie bei Inline-Inhalt. Siehe Templates zum Erstellen und Verwalten von Templates.

Nächste Schritte

Auf dieser Seite