TrackMailer — Dokumentation
Getting Started
1. Erstelle und bestätige genau einen Cloud-Account. 2. Wähle oder erstelle die Organisation, der die Integration gehört. 3. Lege ein Projekt für die Website, den Shop oder die Marke an, deren Daten zusammenbleiben sollen. 4. Öffne den Produkt-Workspace und prüfe die Freischaltung, bevor du Zugangsdaten kopierst oder Produktionsverkehr sendest.
Damit ist die Account-Einrichtung abgeschlossen. Alle folgenden Abschnitte sind Implementierungsanleitungen. Zugangsdaten sind immer projektgebunden; Server-Secrets gehören niemals in Browsercode, öffentliche Repositories oder clientseitige Umgebungsvariablen.
Vor der Integration
Transaktionaler Versand ist nur für freigeschaltete Organisationen mit aktiver Subscription oder Servicefreigabe, verifizierter Absenderdomain und aktivem Live-Key verfügbar. Kontakt-Schreibzugriffe benötigen den Scope contacts:write. Sandbox-Anfragen prüfen dieselben Projektverträge, ohne Kontakte zu speichern oder E-Mails zuzustellen.
API-Key erstellen und sicher speichern
Öffne im Cloud-Workspace TrackMailer → API-Keys. Ein Organisationsinhaber oder Administrator wählt Sandbox oder Live, die kleinsten benötigten Scopes, erlaubte Absenderdomains und eine Laufzeit von 1–365 Tagen. Der vollständige Token wird einmal angezeigt; speichere ihn im Secret Manager des aufrufenden Servers.
Setze TRACKMAILER_API_URL auf https://us-east4-markengroup-cloud-services.cloudfunctions.net/trackmailerApi. Nutze contacts:write für Kontakte, transactional:send für transaktionalen Versand und sequences:enroll für Newsletter-Gruppen-Sequenzen. Speichere den Token auf dem Server; prüfe einen Ersatz vor dem Widerruf des alten Keys. Sende Authorization: Bearer <token> mit JSON-Requests.
Kontakt anlegen oder aktualisieren
Sende die Operation contacts.upsert per POST an $TRACKMAILER_API_URL/v1/requests. Ein Live-Key speichert den projektgebundenen Kontakt; ein Sandbox-Key antwortet mit status=validated, sent=false und persisted=false. Die E-Mail identifiziert den Kontakt innerhalb des Projekts, externalId kann die stabile ID des Quellsystems enthalten.
Attributes müssen den zuvor im Workspace definierten Feldern entsprechen. Consent benötigt Status, Quelle und einen ISO-8601-Zeitpunkt, der nicht in der Zukunft liegt. Eine erfolgreiche Live-Antwort enthält status=stored, contactId und revision.
curl --request POST "$TRACKMAILER_API_URL/v1/requests" \
--header "Authorization: Bearer $TRACKMAILER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"operation": "contacts.upsert",
"contact": {
"email": "alex@example.com",
"name": "Alex Rivera",
"externalId": "crm_123",
"attributes": {},
"listIds": [
"LIST_ID"
],
"consent": {
"status": "subscribed",
"source": "checkout",
"recordedAt": "2026-09-08T15:00:00Z"
}
}
}'ModelBoost-Kontoeinstellungen synchronisieren
Verwende contacts.sync ausschließlich aus dem vertrauenswürdigen ModelBoostMDE-Backend mit einem contacts:write-Key. externalId ist die stabile Firebase-UID; email ist die aktuelle Adresse und previousEmail der optionale Suchhinweis bei einer Adressänderung. listIds ist der vollständige gewünschte Satz projektzugehöriger Empfängergruppen; nicht enthaltene IDs werden entfernt. Reguläre audience-Gruppen aus dem Workspace und ausdrückliche newsletter-Listen sind erlaubt, andere Listenarten werden abgewiesen.
Die Consent-Quelle muss modelboost_account_preferences sein. Eine neuere ausdrückliche Anmeldung kann eine normale Abmeldung für die gewählten Gruppen aufheben. Bounce-, Complaint- und Missbrauchssperren bleiben wirksam. Identische Wiederholungen liefern contactId und revision unverändert; ältere Präferenzzeitpunkte oder Identitätskonflikte ergeben HTTP 409.
{
"code": "curl --request POST \"$TRACKMAILER_API_URL/v1/requests\" \\\n --header \"Authorization: Bearer $TRACKMAILER_API_KEY\" \\\n --header \"Content-Type: application/json\" \\\n --data '{\n \"operation\": \"contacts.sync\",\n \"contact\": {\n \"externalId\": \"FIREBASE_UID\",\n \"email\": \"alex.new@example.com\",\n \"previousEmail\": \"alex@example.com\",\n \"name\": \"Alex Rivera\",\n \"listIds\": [\n \"RECIPIENT_GROUP_ID\"\n ],\n \"consent\": {\n \"status\": \"subscribed\",\n \"source\": \"modelboost_account_preferences\",\n \"recordedAt\": \"2026-09-22T15:00:00Z\"\n }\n }\n}'",
"language": "bash",
"codeExamples": [
{
"id": "curl",
"label": "cURL",
"language": "bash",
"code": "curl --request POST \"$TRACKMAILER_API_URL/v1/requests\" \\\n --header \"Authorization: Bearer $TRACKMAILER_API_KEY\" \\\n --header \"Content-Type: application/json\" \\\n --data '{\n \"operation\": \"contacts.sync\",\n \"contact\": {\n \"externalId\": \"FIREBASE_UID\",\n \"email\": \"alex.new@example.com\",\n \"previousEmail\": \"alex@example.com\",\n \"name\": \"Alex Rivera\",\n \"listIds\": [\n \"RECIPIENT_GROUP_ID\"\n ],\n \"consent\": {\n \"status\": \"subscribed\",\n \"source\": \"modelboost_account_preferences\",\n \"recordedAt\": \"2026-09-22T15:00:00Z\"\n }\n }\n}'"
},
{
"id": "node",
"label": "Node.js",
"language": "javascript",
"code": "const response = await fetch(`${process.env.TRACKMAILER_API_URL}/v1/requests`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${process.env.TRACKMAILER_API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n \"operation\": \"contacts.sync\",\n \"contact\": {\n \"externalId\": \"FIREBASE_UID\",\n \"email\": \"alex.new@example.com\",\n \"previousEmail\": \"alex@example.com\",\n \"name\": \"Alex Rivera\",\n \"listIds\": [\n \"RECIPIENT_GROUP_ID\"\n ],\n \"consent\": {\n \"status\": \"subscribed\",\n \"source\": \"modelboost_account_preferences\",\n \"recordedAt\": \"2026-09-22T15:00:00Z\"\n }\n }\n }),\n});\n\nif (!response.ok) throw new Error(`Request failed: ${response.status}`);\nconsole.log(await response.json());"
},
{
"id": "python",
"label": "Python",
"language": "python",
"code": "import os\nimport requests\n\npayload = {\n \"operation\": \"contacts.sync\",\n \"contact\": {\n \"externalId\": \"FIREBASE_UID\",\n \"email\": \"alex.new@example.com\",\n \"previousEmail\": \"alex@example.com\",\n \"name\": \"Alex Rivera\",\n \"listIds\": [\n \"RECIPIENT_GROUP_ID\",\n ],\n \"consent\": {\n \"status\": \"subscribed\",\n \"source\": \"modelboost_account_preferences\",\n \"recordedAt\": \"2026-09-22T15:00:00Z\",\n },\n },\n}\n\nresponse = requests.post(\n f\"{os.environ['TRACKMAILER_API_URL']}/v1/requests\",\n headers={\n \"Authorization\": f\"Bearer {os.environ['TRACKMAILER_API_KEY']}\",\n \"Content-Type\": \"application/json\",\n },\n json=payload,\n timeout=10,\n)\nresponse.raise_for_status()\nprint(response.json())"
},
{
"id": "php",
"label": "PHP",
"language": "php",
"code": "<?php\n\n$payload = [\n 'operation' => 'contacts.sync',\n 'contact' => [\n 'externalId' => 'FIREBASE_UID',\n 'email' => 'alex.new@example.com',\n 'previousEmail' => 'alex@example.com',\n 'name' => 'Alex Rivera',\n 'listIds' => [\n 'RECIPIENT_GROUP_ID',\n ],\n 'consent' => [\n 'status' => 'subscribed',\n 'source' => 'modelboost_account_preferences',\n 'recordedAt' => '2026-09-22T15:00:00Z',\n ],\n ],\n];\n$curl = curl_init(getenv('TRACKMAILER_API_URL') . '/v1/requests');\n\ncurl_setopt_array($curl, [\n CURLOPT_POST => true,\n CURLOPT_RETURNTRANSFER => true,\n CURLOPT_HTTPHEADER => [\n 'Authorization: Bearer ' . getenv('TRACKMAILER_API_KEY'),\n 'Content-Type: application/json',\n ],\n CURLOPT_POSTFIELDS => json_encode($payload, JSON_THROW_ON_ERROR),\n CURLOPT_TIMEOUT => 10,\n]);\n\n$response = curl_exec($curl);\nif ($response === false) {\n throw new RuntimeException(curl_error($curl));\n}\n$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);\ncurl_close($curl);\nif ($status >= 400) {\n throw new RuntimeException(\"Request failed: $status\");\n}\necho $response;"
}
]
}Zu einer Liste hinzufügen oder entfernen
Erstelle die manuelle Liste unter TrackMailer → Zielgruppen → Gruppen und kopiere ihre angezeigte Listen-ID. contacts.upsert bleibt bewusst additiv: spätere listIds werden mit vorhandenen Mitgliedschaften zusammengeführt; ein leeres Array entfernt keine bisherigen Listen.
Nur contacts.sync ist als eng begrenzter Replace-Membership-Vertrag für die ModelBoostMDE-Kontoeinstellungen vorgesehen. Andere Integrationen verwenden weiterhin contacts.upsert oder das Dashboard und dürfen nicht annehmen, dass der additive Upsert Mitgliedschaften entfernt.
Abmeldung und Suppression
Um eine Abmeldung aus einem eigenen Preference Center zu erfassen, sende contacts.upsert mit consent.status=unsubscribed sowie Quelle und Zeitpunkt. Das sperrt den Kontakt und ist etwas anderes als das Entfernen einer Listenmitgliedschaft. Bereits abgemeldete, gebouncte oder beanstandete Kontakte werden durch einen späteren Import niemals reaktiviert.
curl --request POST "$TRACKMAILER_API_URL/v1/requests" \
--header "Authorization: Bearer $TRACKMAILER_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"operation": "contacts.upsert",
"contact": {
"email": "alex@example.com",
"name": "Alex Rivera",
"attributes": {},
"listIds": [],
"consent": {
"status": "unsubscribed",
"source": "preference-center",
"recordedAt": "2026-09-08T16:00:00Z"
}
}
}'Absenderdomain vorbereiten
Der Workspace akzeptiert automatisch die Domain der bestätigten Login-E-Mail und ihre Subdomains; für die Einrichtung ist keine Plattform-Admin-Freigabe nötig. Füge unter TrackMailer → Absenderdomains den exakten Hostnamen wie mail.example.com hinzu. Veröffentliche den projektbezogenen Eigentums-TXT sowie die angezeigten DKIM-, MAIL-FROM-MX- und SPF-Einträge und warte auf die vollständige Verifizierung. Verwende die angezeigte 64-stellige senderDomainId in Keys und Versandrequests.
Nutze je Marke oder relevantem Versandstrom eine dedizierte Subdomain. Jeder Absenderhost benötigt weiterhin seinen eigenen Projektnachweis und die vollständige Verifizierung; ein API-Key kann nur über ausdrücklich zugeordnete senderDomainIds versenden.
Anti-Spam Health: DKIM, SPF und DMARC
TrackMailer zeigt Domain-Bereitschaft, Authentifizierung und Zustellsignale. Übernimm DNS-Werte exakt und stimme die DMARC-Richtlinie mit der Mail-Administration ab. Authentifizierung reduziert vermeidbare Klassifizierungsprobleme, kann aber den Posteingang nicht garantieren.
Transaktionale E-Mail versenden
Sende per POST von einem vertrauenswürdigen Server an $TRACKMAILER_API_URL/v1/email/send. Der Live-Key benötigt transactional:send und muss die gewählte senderDomainId erlauben. Der Request unterstützt 1–20 eindeutige Empfänger, HTML und Plaintext, eine optionale Reply-to-Adresse sowie die Zwecke receipt, security oder notification.
Setze für die fachliche Nachricht einen eindeutigen Idempotency-Key-Header mit 8–128 Zeichen. Derselbe Key mit identischem Body liefert dieselbe messageId, ohne erneut zuzustellen; derselbe Key mit anderem Inhalt erzeugt HTTP 409 idempotency_conflict. Ein neuer angenommener Request antwortet mit HTTP 202 und status=queued.
curl --request POST "$TRACKMAILER_API_URL/v1/email/send" \
--header "Authorization: Bearer $TRACKMAILER_API_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: order_8472_confirmation_v1" \
--data '{
"senderDomainId": "SENDER_DOMAIN_ID",
"fromLocalPart": "orders",
"fromName": "Example Shop",
"to": [
{
"email": "alex@example.com",
"name": "Alex Rivera"
}
],
"replyTo": {
"email": "support@example.com",
"name": "Support"
},
"subject": "We received your order",
"html": "<p>Thanks for your order.</p>",
"text": "Thanks for your order.",
"purpose": "receipt",
"tags": {
"MessageType": "order_confirmation",
"Environment": "production"
}
}'Vor dem Versand validieren
Nutze einen Sandbox-Key gegen /v1/requests für contacts.upsert, contacts.sync, transactional.send, sequence.enroll. Die Sandbox prüft Schema, Scope und Projektressourcen, speichert, sendet und plant aber nichts. Produktive Versandendpunkte verlangen einen Live-Key.
Responses, Status und Retries
Speichere requestId und messageId jeder angenommenen Sendung. Prüfe im TrackMailer-Bereich Versand die Zustände queued, sent, delivered, bounced, complained, suppressed, failed oder uncertain. HTTP 202 bedeutet dauerhaft eingereiht, nicht zugestellt und nicht garantiert im Posteingang.
Wiederhole 429 erst nach dem Retry-After-Wert. Wiederhole vorübergehende 503-Fehler mit begrenztem exponentiellem Backoff sowie demselben Idempotency-Key und Body. Wiederhole 400, 401, 403, 402 oder 409 erst, nachdem Validierung, Key, Scope, Absender, Berechtigung, Billing oder Konflikt korrigiert wurden.