Die Heymail API ist derzeit nur auf Anfrage verfügbar. Bitte kontaktieren Sie uns, um Ihren Zugang freischalten zu lassen.

Public Mailings API v1

Postkarten per API versenden

Heymail API-Dokumentation für den Versand von Postkarten in eigene Anwendungen und Prozesse.

Basis-URL
https://api.heymail.com/v1
Endpoints

1 · Grundlagen

Mit der API lässt sich der Postkartenversand direkt in eigene Anwendungen, Shops, Backends oder interne Prozesse integrieren. Die API übernimmt Vorschau, Einzel- und Batchversand. Gestaltung und Pflege der Templates sowie die Verwaltung erzeugter Aufträge erfolgen weiterhin in der Heymail Webanwendung.

  1. Templates gestalten

    In der Heymail-App werden Vorder- und Rückseite der Postkarte gestaltet. Platzhalter für variable Inhalte werden direkt im Template hinterlegt, z. B. {{couponCode}} oder {{firstName}}. Nach dem Speichern ist das Template über seine templateId in der API referenzierbar.

  2. API-Requests senden

    Für Vorschauen steht POST /v1/mailings/preview zur Verfügung. Für den Versand wird POST /v1/mailings/send verwendet — egal ob eine einzelne Postkarte oder ein Mailing an viele Empfänger; mailItems ist immer ein Array. Jedes Item kombiniert ein Anschreiben aus recipient (Adresse) und optional variableData (Template-Variablen) als Geschwister.

  3. Aufträge nachverfolgen

    API-basierte Aufträge sind in der Heymail-App unter Mailings sichtbar. Dort lassen sich unter anderem Status, Empfänger und das verwendete Template nachvollziehen.

Abrechnung:
Requests mit einem live-Key erzeugen produktive Aufträge. Requests mit einem test-Key erzeugen keine abrechenbaren Sendungen.

2 · Quickstart

Der Einstieg läuft typischerweise in vier Schritten:

  1. API-Zugang anlegen

    Für Entwicklung und Tests einen Key mit Umgebung test anlegen. Der Key wird nur einmal vollständig angezeigt und sollte direkt sicher gespeichert werden.

    API verwalten

  2. Template anlegen

    Das Template in der Heymail-App erstellen, speichern und die templateId kopieren. Diese ID wird in allen API-Requests verwendet.

    Zu den Vorlagen

  3. Preview generieren

    Vor dem ersten Versand empfiehlt sich ein Preview-Request. Damit lässt sich prüfen, ob Template und variable Daten korrekt zusammenspielen.

    curl -X POST https://api.heymail.com/v1/mailings/preview \
          -H "Authorization: Bearer hey-test-..." \
          -H "Content-Type: application/json" \
          -d '{
            "templateId": "DEINE_TEMPLATE_ID",
            "mailItem": {
              "recipient": {
                "lastName": "Mustermann",
                "street": "Testweg",
                "houseNumber": "5",
                "zip": "50667",
                "city": "Köln"
              },
              "variableData": {
                "couponCode": "SPRING-2026"
              }
            }
          }'
  4. Testauftrag auslösen

    Mit einem test-Key einen ersten Versand auslösen. Bei Erfolg liefert die API 201 Created und eine orderId. Der Auftrag ist anschließend unter Mailings sichtbar.

    curl -X POST https://api.heymail.com/v1/mailings/send \
          -H "Authorization: Bearer hey-test-..." \
          -H "Content-Type: application/json" \
          -d '{
            "templateId": "DEINE_TEMPLATE_ID",
            "mailItems": [
              {
                "recipient": {
                  "salutation": "Herr",
                  "firstName": "Max",
                  "lastName": "Mustermann",
                  "street": "Testweg",
                  "houseNumber": "5",
                  "zip": "50667",
                  "city": "Köln",
                  "country": "Deutschland"
                },
                "variableData": {
                  "couponCode": "SPRING-2026"
                }
              }
            ]
          }'

Basis-URL:

https://api.heymail.com/v1

3 · Authentifizierung

Jeder Request wird mit einem persönlichen API-Key als Bearer-Token authentifiziert. Der Key wird im Authorization-Header übertragen:

Authorization: Bearer hey-live-abc...
Test-Keyhey-test-xxx — für Entwicklung, CI und Integrationstests. Es wird kein produktiver Auftrag erzeugt.
Live-Keyhey-live-xxx — für produktive Requests. Erfolgreiche Requests erzeugen echte Aufträge und Sendungen.
Scopes (Default)mailings:send und mailings:preview sind standardmäßig aktiv.
VoraussetzungDer Account des API-Keys muss verifiziert und für die Public API freigeschaltet sein. Andernfalls antwortet die API mit 403 AUTHORIZATION.

4 · Endpoint: Preview

POST/v1/mailings/preview

Erzeugt eine kurz gültige Preview-URL für die Postkarte mit den übergebenen Daten. Der Request erzeugt keinen Auftrag, keinen Druck und keinen Versand. Symmetrisch zu /send: gleiche templateId/sender-Felder, gleiche MailItem-Struktur — nur statt mailItems[] ein einzelnes mailItem.

Request
{
      "templateId": "22222222-2222-4222-8222-222222222222",
      "mailItem": {
        "recipient": {
          "lastName": "Mustermann",
          "street": "Testweg",
          "zip": "50667",
          "city": "Köln"
        },
        "variableData": {
          "couponCode": "SPRING-2026"
        }
      }
    }
Response 200 OK

    {
      "previewUrl": "https://..."
    }

Preview-URLs sind nur kurz gültig und sollten nicht dauerhaft gespeichert werden. Bei Bedarf kann jederzeit eine neue Vorschau angefordert werden.

5 · Endpoint: Send

POST/v1/mailings/send

Erzeugt einen Auftrag für ein Mailing an einen oder viele Empfänger. Egal ob 1 oder 10000 Anschreiben — mailItems ist immer ein Array, und der Request erzeugt genau einen Auftrag. Jedes Item kombiniert ein Anschreiben aus recipient (Adresse) und optional variableData (Template-Variablen) als Geschwister. Wenn ein einzelner Eintrag ungültig ist, wird der gesamte Request abgelehnt.

Request — einzelnes Anschreiben
{
      "templateId": "22222222-2222-4222-8222-222222222222",
      "shippingDate": "2026-04-20",
      "mailItems": [
        {
          "recipient": {
            "salutation": "Herr",
            "firstName": "Max",
            "lastName": "Mustermann",
            "street": "Testweg",
            "houseNumber": "5",
            "zip": "50667",
            "city": "Köln",
            "country": "Deutschland"
          },
          "variableData": {
            "couponCode": "SPRING-2026"
          }
        }
      ],
      "sender": {
        "company": "Heymail",
        "street": "Beispielweg",
        "houseNumber": "1",
        "zip": "50667",
        "city": "Köln"
      }
    }
Request — viele Anschreiben
{
      "templateId": "22222222-2222-4222-8222-222222222222",
      "name": "Spring campaign",
      "mailItems": [
        {
          "recipient": {
            "lastName": "Mustermann",
            "street": "Testweg",
            "zip": "50667",
            "city": "Köln"
          },
          "variableData": { "couponCode": "SPRING-2026" }
        },
        {
          "recipient": {
            "company": "ACME GmbH",
            "street": "Business Park 1",
            "zip": "10115",
            "city": "Berlin"
          },
          "variableData": { "couponCode": "SPRING-2026" }
        }
      ]
    }
Response 201 Created
{
      "orderId": "77777777-7777-4777-8777-777777777777",
      "status": "ACCEPTED",
      "recipientCount": 2
    }
PflichtfeldertemplateId, mailItems (Array, 1–10000 Einträge); pro Item recipient mit street, zip, city sowie lastName oder company
Optionalsender (sonst Default-Sender), shippingDate (sonst nächster gültiger Versandtag), name (interner Name des Mailings), mailItems[i].variableData (Template-Variablen pro Anschreiben)
WichtigmailItems ist auf maximal 10000 Einträge begrenzt. Ein ungültiger Eintrag macht den gesamten Request ungültig.

6 · Sender und Default-Sender

Jeder Versandauftrag benötigt einen Absender. Dafür gibt es zwei Möglichkeiten:

  • Sender pro Request mitsenden — das sender-Objekt wird direkt aus dem Request übernommen.
  • Default-Sender verwenden — wenn sender fehlt, verwendet die API automatisch den im Account hinterlegten Default-Sender.
  • Wenn weder sender gesetzt noch ein Default-Sender hinterlegt ist, antwortet die API mit 422 VALIDATION.
  • Für den Absender gelten dieselben Pflichtfelder wie für Empfänger: street, zip, city sowie entweder company oder firstName und lastName.
  • Der Default-Sender kann im Account unter Einstellungen > Absender gepflegt werden.

Der Absender wird nicht auf Ihre Postkarte gedruckt und nur fuer die postalische Einlieferung benoetigt.

7 · Template-Variablen (variableData)

Templates können Platzhalter für dynamische Inhalte enthalten. Die zugehörigen Werte werden pro Anschreiben unter mailItems[].variableData als Geschwister neben recipient übergeben:

"mailItems": [
      {
        "recipient": {
          "lastName": "Mustermann",
          "street": "Testweg",
          "zip": "50667",
          "city": "Köln"
        },
        "variableData": {
          "couponCode": "SPRING-2026",
          "postTitle": "Fruehlingsaktion"
        }
      }
    ]
  • Maximal 2048 Zeichen pro Wert (Bilder werden als URL uebergeben). Die Gesamtgröße des Requests bleibt durch das Payload-Limit von 2 MB begrenzt.
  • Nur Variablen, die im Template hinterlegt sind, sollten übergeben werden. Zusätzliche Keys haben aktuell keine Wirkung.
  • variableData ist Geschwister von recipient innerhalb eines Items — nicht in das recipient-Objekt schachteln und nicht auf Top-Level setzen.

8 · Fehlermodell

Alle Fehlerantworten verwenden dasselbe Schema. Neben einer lesbaren message liefert die API immer eine requestId, damit sich Anfragen und Fehler eindeutig zuordnen lassen:

{
      "type": "VALIDATION",
      "message": "shippingDate is invalid.",
      "requestId": "6da3e10d-3b2f-4f1a-9a7c-4b8f0d1e2c3a",
      "errors": [
        {
          "field": "shippingDate",
          "code": "RESTRICTED_DAY",
          "message": "shippingDate is not available for shipping."
        }
      ]
    }
StatusTypeTypische Ursache
400VALIDATIONUngültiges Request-Format, Pflichtfeld fehlt, Malformed JSON oder nicht erlaubtes Top-Level-Feld
401AUTHENTICATIONKein oder ungültiger API-Key, widerrufener Key
403AUTHORIZATIONAccount unverifiziert, Public API nicht freigeschaltet, fehlender Scope oder Origin geblockt (CORS)
413PAYLOAD_TOO_LARGERequest-Body > 2 MB
415UNSUPPORTED_MEDIA_TYPEContent-Type nicht application/json
422VALIDATIONFachliche Validierung (Sender fehlt, shippingDate, Checkout)
429RATE_LIMITRate-Limit überschritten
500INTERNALServerfehler (keine Details nach außen)
Feld-Fehlercodes (errors[].code)

Bei Validierungsfehlern enthält errors pro betroffenem Feld einen maschinenlesbaren code. Clients sollten diesen Code auswerten, statt nur die message zu parsen:

  • UNKNOWN_FIELD — ein nicht erlaubtes Feld wurde mitgeschickt.
  • index — verweist auf das fehlerhafte Item im mailItems-Array (0-basiert). Die field-Pfade recipient.<feld> bzw. variableData.<key> machen sichtbar, ob die Adresse oder die Template-Variablen das Problem sind.
  • INVALID_FORMAT — das Feld hat das falsche Format, z. B. shippingDate nicht als YYYY-MM-DD.
  • BEFORE_NEXT_SHIPPING_DATEshippingDate liegt vor dem nächsten möglichen Versandtag.
  • RESTRICTED_DAY — der gewählte Tag ist ein Wochenende oder Feiertag und damit nicht versandfähig.
  • OUT_OF_RANGEshippingDate liegt mehr als ein Jahr in der Zukunft.

9 · Limits

Die folgenden Limits gelten für alle Requests. Bei Überschreitung antwortet die API mit dem passenden HTTP-Status und dem dokumentierten Fehlerobjekt:

Rate-Limit /send5 Requests pro Minute und Key (ein Request darf bis zu 10000 Empfänger enthalten)
Rate-Limit /preview30 Requests pro Minute und Key
Rate-Limit-HeaderX-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
Max. Request-Body2 MB (> 2 MB → 413)
Max. Empfängeranzahl10000 Empfänger pro send-Request
Max. Wertlänge2048 Zeichen pro variableData-Value
Max. Adressfeld-Länge256 Zeichen pro Feld
Request-ID-HeaderX-Request-ID (eingehend optional, ausgehend immer gesetzt)

10 · curl-Beispiele

Die folgenden Beispiele können direkt als Ausgangspunkt verwendet werden. Dazu den Test-Key und die templateId durch reale Werte aus dem eigenen Account ersetzen:

Preview erzeugen
curl -X POST https://api.heymail.com/v1/mailings/preview \
      -H "Authorization: Bearer hey-test-abc..." \
      -H "Content-Type: application/json" \
      -d '{
        "templateId": "22222222-2222-4222-8222-222222222222",
        "mailItem": {
          "recipient": {
            "lastName": "Mustermann",
            "street": "Testweg",
            "zip": "50667",
            "city": "Köln"
          },
          "variableData": {
            "couponCode": "SPRING-2026"
          }
        }
      }'
Postkarte versenden (Test-Key)
curl -X POST https://api.heymail.com/v1/mailings/send \
      -H "Authorization: Bearer hey-test-abc..." \
      -H "Content-Type: application/json" \
      -d '{
        "templateId": "22222222-2222-4222-8222-222222222222",
        "mailItems": [
          {
            "recipient": {
              "salutation": "Herr",
              "firstName": "Max",
              "lastName": "Mustermann",
              "street": "Testweg",
              "houseNumber": "5",
              "zip": "50667",
              "city": "Köln",
              "country": "Deutschland"
            },
            "variableData": {
              "couponCode": "SPRING-2026"
            }
          }
        ]
      }'
Mailing an viele Anschreiben versenden
curl -X POST https://api.heymail.com/v1/mailings/send \
      -H "Authorization: Bearer hey-test-abc..." \
      -H "Content-Type: application/json" \
      -d '{
        "templateId": "22222222-2222-4222-8222-222222222222",
        "mailItems": [
          {
            "recipient": {
              "lastName": "Mustermann",
              "street": "Testweg",
              "zip": "50667",
              "city": "Köln"
            },
            "variableData": { "couponCode": "SPRING-2026" }
          },
          {
            "recipient": {
              "company": "ACME GmbH",
              "street": "Business Park 1",
              "zip": "10115",
              "city": "Berlin"
            },
            "variableData": { "couponCode": "SPRING-2026" }
          }
        ]
      }'
Fehler debuggen über X-Request-ID
curl -i -X POST https://api.heymail.com/v1/mailings/send \
      -H "Authorization: Bearer hey-test-abc..." \
      -H "Content-Type: application/json" \
      -H "X-Request-ID: $(uuidgen)" \
      -d '{ "templateId": "..." }'