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/sendcurl -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
{
"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
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
from | object | Ja | Absenderadresse |
from.email | string | Ja | Absender-E-Mail (muss auf einer verifizierten Domain sein) |
from.name | string | Nein | Anzeigename |
to | array | Ja | Empfängerliste |
to[].email | string | Ja | E-Mail-Adresse des Empfängers |
to[].name | string | Nein | Anzeigename des Empfängers |
to[].variables | object | Nein | Empfängerspezifische Template-Variablen |
subject | string | Bedingt | Erforderlich, sofern kein Template verwendet wird |
templateId | string | Nein | Gespeichertes Template statt Inline-Inhalt verwenden |
htmlContent | string | Bedingt | HTML-Körper (htmlContent oder textContent erforderlich, wenn kein templateId vorhanden ist) |
textContent | string | Nein | Nur-Text-Fallback |
variables | object | Nein | Globale Template-Variablen. Werden unabhängig davon angewendet, ob templateId gesetzt ist; bei überschneidenden Schlüsseln gewinnt der empfängereigene Wert aus to[].variables |
replyTo | string | Nein | Antwort-E-Mail-Adresse |
tags | array | Nein | Tags zum Filtern in Analysen und Webhooks |
customHeaders | object | Nein | Benutzerdefinierte E-Mail-Header (Schlüssel-Wert-Paare) |
tracking | object | Nein | Tracking-Einstellungen überschreiben |
tracking.opens | boolean | Nein | Ö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.clicks | boolean | Nein | Klick-Tracking aktivieren (Standard: false für Transaktions-Streams, sofern Ihr Konto es nicht aktiviert, true für Marketing) |
ipPoolId | string | Nein | Reserviert für dedizierte IP-Pools. Noch nicht verfügbar – wird es gesendet, kommt 400 IP_POOL_NOT_FOUND zurück. Weglassen. |
scheduledAt | datetime | Nein | Für zukünftige Zustellung planen (UTC), bis zu 7 Tage im Voraus |
idempotencyKey | string | Nein | Doppelten Versand für denselben Schlüssel verhindern. Bevorzugt den Header X-Idempotency-Key |
attachments | array | Nein | Dateianhänge (max. 10, max. 10 MB dekodiert insgesamt) |
priority | enum | Nein | Low, Normal (Standard), High oder Critical |
Anhänge
Jeder Anhang erfordert drei Felder:
{
"attachments": [
{
"fileName": "invoice.pdf",
"contentType": "application/pdf",
"content": "JVBERi0xLjQKJ...",
"inline": false
},
{
"fileName": "logo.png",
"contentType": "image/png",
"content": "iVBORw0KGgoAAAA...",
"contentId": "company-logo",
"inline": true
}
]
}| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
fileName | string | Ja | Dateiname mit Erweiterung |
contentType | string | Ja | MIME-Typ (z.B. application/pdf) |
content | string | Ja | Standard-Base64-kodierter Dateiinhalt – mit Padding, ohne Leerzeichen oder Zeilenumbrüche |
contentId | string | Nein | Inhalts-ID für Inline-Anhänge (im HTML als cid:company-logo referenzieren) |
inline | boolean | Nein | Ob 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": true,
"jobId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"messageIds": ["t_a1b2c3d4e5f6..."],
"acceptedRecipients": ["[email protected]"],
"rejectedRecipients": [],
"queuedAt": "2026-02-17T10:30:00Z",
"isSandboxed": false
}| Feld | Typ | Beschreibung |
|---|---|---|
success | boolean | Ob die Anfrage akzeptiert wurde |
jobId | string | Eindeutige Job-ID für die Nachverfolgung |
messageIds | array | Nachrichten-IDs pro Empfänger (t_-Präfix für transaktional, m_ für Marketing) |
acceptedRecipients | array | Zur Zustellung akzeptierte E-Mail-Adressen |
rejectedRecipients | array | Abgelehnte Empfänger |
queuedAt | datetime | Zeitpunkt der Einreihung in die Warteschlange |
isSandboxed | boolean | true, wenn die Anfrage ein Sandbox-Credential verwendet hat (siehe Sandbox-Modus) |
sandboxReason | string | credential_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:
{
"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 erkanntdomain_typo: wahrscheinlicher Tippfehler in der Domain (enthältsuggestion-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=1curl https://api.mail.land/v1/transactional/send/check?recipientCount=1 \
-H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."{
"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-v1Alternativ das Body-Feld idempotencyKey. Beide werden akzeptiert; beide gemeinsam nur bei identischen Werten.
Regeln
- Geltungsbereich.
X-Idempotency-Keywird ausschließlich vonPOST /v1/transactional/sendundPOST /v1/transactional/send-batchgelesen. - 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
409abgelehnt; 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-12345oder
X-Request-ID: my-app-order-12345Die 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: 295Bei Ratenlimitierung (429) durch die Sende-Policy-Engine gibt ein Retry-After-Header die Wartezeit in Sekunden an:
Retry-After: 30Eine 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:
{
"code": "ERROR_CODE",
"message": "Human-readable description",
"timestamp": "2026-02-17T10:30:00Z",
"traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}Fehlercodes
| Status | Code | Beschreibung |
|---|---|---|
400 | INVALID_REQUEST | Fehlerhaft formatierter Anfragekörper, oder scheduledAt mehr als 7 Tage im Voraus ("Cannot schedule email more than 7 days in advance") |
400 | VALIDATION_FAILED | Kein 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.) |
413 | CONTENT_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 |
400 | transactional_recipient_limit_exceeded | Mehr als 5 Empfänger bei einem transaktionalen Zugangsdatensatz (1.000 bei Marketing) |
400 | TEMPLATE_NOT_FOUND | templateId benennt kein aktives Template dieses Kontos (wird auch für ein gelöschtes Template zurückgegeben) |
400 | IP_POOL_NOT_FOUND | ipPoolId benennt keinen diesem Konto zugewiesenen Pool (derzeit immer, da noch kein Pool zugewiesen werden kann, siehe Feldreferenz oben) |
422 | TEMPLATE_VARIABLE_MISSING | Der Inhalt enthält einen {{placeholder}} ohne Wert; details.missingVariables nennt die Namen. Nichts wird eingereiht |
400 | IDEMPOTENCY_KEY_INVALID | Schlüssel ist leer, ungültig, länger als 255 Zeichen, oder der Header wurde mehrfach gesendet |
400 | IDEMPOTENCY_KEY_CONFLICT | Header und Body-Feld idempotencyKey wurden beide gesendet und unterscheiden sich |
400 | SEND_FAILED | Alle Empfänger sind unterdrückt (Meldung: „All recipients are suppressed") |
409 | IDEMPOTENCY_KEY_REUSED | Schlüssel wird bereits für einen anderen Endpunkt dieses Kontos verwendet |
401 | UNAUTHORIZED | X-API-Key fehlt, ist ungültig, abgelaufen oder widerrufen |
403 | ACCOUNT_SUSPENDED | Konto wurde gesperrt |
403 | ACCOUNT_DISABLED | Konto wurde deaktiviert |
403 | ACCOUNT_NOT_ACTIVE | Konto ist nicht aktiv |
403 | NO_ACTIVE_SUBSCRIPTION | Konto hat kein aktives Abonnement |
403 | SUBSCRIPTION_EXPIRED | Abonnement des Kontos ist abgelaufen |
403 | IP_NOT_ALLOWED | Anfrage-IP nicht in der Zulassungsliste der Zugangsdaten |
403 | SENDING_BLOCKED | Versand für dieses Konto ist gesperrt |
403 | ACTION_REQUIRED | Vor dem Versand ist eine Kontoaktion erforderlich |
403 | TENANT_APPROVAL_REQUIRED | Das Konto wartet vor dem Versand auf eine Prüfung. Wiederholen hilft nicht; wenden Sie sich an den Support |
422 | DOMAIN_NOT_VERIFIED | Absender-Domain nicht verifiziert |
422 | DOMAIN_NOT_ALLOWED_FOR_CREDENTIAL | Domain nicht im Gültigkeitsbereich der Zugangsdaten |
422 | DOMAIN_NOT_FOUND | Absender-Domain nicht gefunden |
429 | RATE_LIMIT_EXCEEDED | Ratenlimit überschritten |
429 | QUOTA_EXCEEDED | Monatliches Kontingent erschöpft |
429 | WARMUP_LIMIT_EXCEEDED | Das tägliche Anlauflimit Ihres Kontos für neuen Versand ist erreicht; Zurücksetzung um 00:00 UTC |
429 | DAILY_LIMIT_EXCEEDED | Tageslimit erreicht |
429 | WARMUP_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." |
429 | WARMUP_POOL_EMPTY | "Sending is temporarily paused. It resumes automatically; no action is needed." |
429 | PLATFORM_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." |
429 | PLATFORM_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." |
429 | PROBATION_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." |
429 | PAYMENT_DAILY_LIMIT_EXCEEDED | Tageslimit bei überfälligem Konto erreicht |
429 | MARKETING_PAUSED | Marketing-Versand für dieses Konto pausiert |
429 | SENDING_BLOCKED | Verifizierungsstufe des Kontos erlaubt diesen Versand nicht (Sandbox-/Verifizierungsablehnung; eine harte Versandsperre liefert stattdessen 403, siehe oben) |
503 | SERVICE_UNAVAILABLE | E-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:
{
"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
- Batch-Versand: an mehrere Empfänger gleichzeitig senden
- SMTP-Relay: per SMTP statt REST senden
- Templates: wiederverwendbare E-Mail-Templates erstellen
- Geplante E-Mails: E-Mails für zukünftige Zustellung planen