1. Ausgangslage
Was heute existiert und welche technischen Einschraenkungen gelten
Kriterien fuer 4leads-Kontakte
produkt_interesse hat Wert)Warum kein Zapier?
| Faktor | Zapier | Direkte Integration |
|---|---|---|
| Kosten | Ab ~20 EUR/Monat (Starter) | 0 EUR (oder Cloudflare Worker: kostenlos) |
| Latenz | 1-15 Minuten (Polling-Intervall) | Echtzeit (Webhook) oder <1 Min (Cron) |
| Wartung | Eigenes Dashboard, eigene Fehler | Alles in HubSpot oder dokumentiert |
| Debugging | Zapier-Logs separat | HubSpot Workflow-Logs + 4leads-Logs |
| Abhaengigkeit | Drittanbieter-Ausfall = Sync-Stopp | Kein Mittelsmann |
Technische Einschraenkung: HubSpot Sales Hub Professional
| Feature | Verfuegbar? |
|---|---|
| Workflows (Contact/Deal-basiert) | Ja |
| Workflow-Trigger auf Property-Aenderung | Ja |
| Outgoing Webhooks aus Workflows | Nein (Data Hub Pro) |
| Incoming Webhook-Trigger | Nein (Data Hub Pro) |
| Custom Code in Workflows | Nein (Data Hub Pro) |
| CRM API (Contacts erstellen/updaten) | Ja |
| Forms API (Daten empfangen) | Ja |
Konsequenz: Fuer 4leads → HubSpot nutzen wir die Forms API. Fuer HubSpot → 4leads gibt es 3 Varianten (siehe Abschnitt 3).
2. Richtung 1: 4leads → HubSpot (Forms API)
Echtzeit-Webhook ueber unsichtbares HubSpot-Formular
2.1 Unsichtbares HubSpot-Formular
Name: [SYSTEM] 4leads Webhook Empfaenger -- wird NICHT auf einer Website eingebettet, dient ausschliesslich als API-Endpunkt.
| Feld | HubSpot-Property | Typ | Zweck |
|---|---|---|---|
| Kontakt-Identifikation (Pflicht) | |||
| 4leads Interaktionstyp | fl_interaktionstyp | Dropdown | Was ist passiert? |
| 4leads Kampagnenname | fl_kampagnenname | Text | Welche Kampagne? |
| 4leads Interaktionsdatum | letzte_4leads_interaktion | Datum | Wann? |
| 4leads Kontakt-ID | fl_kontakt_id | Text | Mapping-ID |
2.2 Neue HubSpot-Properties
Property-Gruppe: 4leads Integration
| Property | Interner Name | Typ | Werte |
|---|---|---|---|
| 4leads Interaktionstyp | fl_interaktionstyp | Dropdown | Email Opened / Link Clicked / Unsubscribed / Bounced / Campaign Completed |
| 4leads Kampagnenname | fl_kampagnenname | Einzeiliger Text | -- |
| Letzte 4leads-Interaktion | letzte_4leads_interaktion | Datum | -- |
| 4leads Kontakt-ID | fl_kontakt_id | Einzeiliger Text | -- |
| 4leads Sync-Status | fl_sync_status | Dropdown | Synced / Pending / Error |
2.3 Webhook-URL fuer 4leads
Beispiel-Payload (JSON):
{ "fields": [ { "name": "email", "value": "max.mustermann@firma.de" }, { "name": "fl_interaktionstyp", "value": "Link Clicked" }, { "name": "fl_kampagnenname", "value": "FMEA-Serie Q1 2026" }, { "name": "letzte_4leads_interaktion", "value": "2026-03-09" }, { "name": "fl_kontakt_id", "value": "4L-12345" } ], "context": { "hutk": "", "pageUri": "https://4leads.de/webhook", "pageName": "4leads Webhook" } }
Content-Type: application/json
2.4 Integrations-Workflows (3 Stueck)
4leads-Interaktion verarbeiten
Einstellungen
Actions (If/Then Branch)
lead_score_custom +5lead_score_custom +15fl_sync_status = "Synced"4leads-Reaktivierung → Sales
Einstellungen
Actions (4 Schritte)
sales_status = "Opening Lead"kontakt_segment = "Hoch"4leads-Unsubscribe verarbeiten
Einstellungen
Actions (3 Schritte)
in_4leads_migriert = "Nein"fl_sync_status = "Synced"2.5 In 4leads konfigurieren: Events → HubSpot
| 4leads-Event | fl_interaktionstyp | Score | Weitere Aktion |
|---|---|---|---|
| E-Mail geoeffnet | email_opened | +5 | -- |
| Link geklickt | link_clicked | +15 | Falls Status Kalt: Reaktivierung → Sales |
| Abmeldung | unsubscribed | 0 | in_4leads_migriert → Nein, Sync gestoppt |
| E-Mail gebounced | bounced | 0 | Nur Logging, kein Score-Impact |
| Kampagne abgeschlossen | campaign_completed | 0 | Info fuer Tim: Serie durchlaufen |
WICHTIG: Die Werte muessen EXAKT so geschrieben werden (Kleinbuchstaben, Unterstriche). HubSpot erkennt sie sonst nicht.
Pflichtfeld in jedem Webhook: email (E-Mail-Adresse des Kontakts) -- damit HubSpot den richtigen Kontakt zuordnen kann.
3. Richtung 2: HubSpot → 4leads (3 Varianten)
HubSpot Sales Hub Professional kann keine Webhooks aus Workflows senden. Wenn ein Kontakt als in_4leads_migriert = Ja markiert wird, muss die Information anderweitig zu 4leads gelangen.
Manueller Export ueber Aktive Liste
Setup
[NEU] 4leads Sync QueueFilter: in_4leads_migriert = Ja UND fl_sync_status != "Synced"
Bewertung
Cloudflare Worker als automatische Bruecke
Cloudflare Worker Code (worker.js)
// worker.js -- HubSpot → 4leads Sync Bridge // Laeuft als Cron-Trigger alle 15 Minuten const HUBSPOT_API_KEY = "pat-xxx"; const FOURLEADS_WEBHOOK_URL = "https://app.4leads.de/webhook/xxx"; export default { async scheduled(event, env, ctx) { await syncContacts(env); }, // Optional: Manueller Trigger via HTTP async fetch(request, env, ctx) { if (request.headers.get("X-Sync-Key") !== env.SYNC_SECRET) { return new Response("Unauthorized", { status: 401 }); } const result = await syncContacts(env); return new Response(JSON.stringify(result)); } }; async function syncContacts(env) { const results = { synced: 0, errors: 0, contacts: [] }; // 1. Hole Kontakte mit sync_status = "Pending" const searchPayload = { filterGroups: [{ filters: [ { propertyName: "in_4leads_migriert", operator: "EQ", value: "Ja" }, { propertyName: "fl_sync_status", operator: "EQ", value: "Pending" } ] }], properties: [ "email", "firstname", "lastname", "company", "norm_standard", "dienstleistung_interesse", "produkt_interesse", "sales_status", "kundenkategorie", "lead_score_custom" ], limit: 50 }; const searchResponse = await fetch( "https://api.hubapi.com/crm/v3/objects/contacts/search", { method: "POST", headers: { "Authorization": `Bearer ${env.HUBSPOT_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify(searchPayload) } ); const searchData = await searchResponse.json(); if (!searchData.results?.length) { return { ...results, message: "Keine neuen Kontakte" }; } // 2. Fuer jeden Kontakt: An 4leads senden for (const contact of searchData.results) { try { const payload = { email: contact.properties.email, firstname: contact.properties.firstname, lastname: contact.properties.lastname, company: contact.properties.company, tags: [ contact.properties.norm_standard, contact.properties.dienstleistung_interesse, contact.properties.sales_status ].filter(Boolean), custom_fields: { hubspot_id: contact.id, kundenkategorie: contact.properties.kundenkategorie, lead_score: contact.properties.lead_score_custom } }; const res = await fetch(env.FOURLEADS_WEBHOOK_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); // 3. Status in HubSpot aktualisieren await fetch( `https://api.hubapi.com/crm/v3/objects/contacts/${contact.id}`, { method: "PATCH", headers: { "Authorization": `Bearer ${env.HUBSPOT_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ properties: { fl_sync_status: res.ok ? "Synced" : "Error" } }) } ); res.ok ? results.synced++ : results.errors++; } catch (e) { results.errors++; } } return results; }
Cloudflare Cron-Trigger (wrangler.toml)
name = "qmh-hubspot-4leads-sync" main = "worker.js" compatibility_date = "2026-03-01" [triggers] crons = ["*/15 * * * *"] # Alle 15 Minuten [vars] HUBSPOT_PORTAL_ID = "143675260" # Secrets (via wrangler secret put): # HUBSPOT_API_KEY # FOURLEADS_WEBHOOK_URL # SYNC_SECRET
HubSpot Workflow: Sync-Vorbereitung
Der Worker holt dann automatisch alle "Pending"-Kontakte und synct sie.
Bewertung
Voraussetzungen
Einzelner Zapier-Zap (minimaler Zapier-Einsatz)
Zap-Konfiguration
Bewertung
Zapier Action 1 -- Payload-Mapping an 4leads:
{ "email": "{{contact.email}}", "firstname": "{{contact.firstname}}", "lastname": "{{contact.lastname}}", "company": "{{contact.company}}", "tags": "{{contact.norm_standard}},{{contact.dienstleistung_interesse}}", "custom_fields": { "hubspot_id": "{{contact.hs_object_id}}", "kundenkategorie": "{{contact.kundenkategorie}}", "lead_score": "{{contact.lead_score_custom}}", "sales_status": "{{contact.sales_status}}" } }
Zapier-Plan-Anforderungen
| Plan | Preis | Tasks/Monat | Reicht? |
|---|---|---|---|
| Free | 0 EUR | 100 | Ja |
| Starter | ~20 EUR/Monat | 750 | Ja |
| Professional | ~50 EUR/Monat | 2.000 | Unnoetig |
Realistisch: Bei QMH werden geschaetzt <20 neue Kontakte pro Monat zu 4leads gesynct. Der Zapier Free Plan reicht.
4. Variantenvergleich: HubSpot → 4leads
Vollstaendige Gegenuberstellung aller drei Varianten
| Kriterium | A: Manuell | B: Cloudflare Worker | C: Zapier-Zap |
|---|---|---|---|
| Setup-Aufwand | 15 Min | 2-3 Stunden | 30 Min |
| Laufende Kosten | 0 EUR | 0 EUR | 0-20 EUR/Mon |
| Automatisierung | Manuell (1x/Woche) | Automatisch (15 Min) | Automatisch (Echtzeit) |
| Technisches Know-how | Keins | JavaScript + CF | Zapier-Grundlagen |
| Wartung | Keine | Worker-Code pflegen | Zap ueberwachen |
| Debugging | CSV pruefen | Worker-Logs | Zapier-Dashboard |
| Ausfallsicherheit | Abhaengig von Tim | CF: 99.9% Uptime | Zapier: 99.9% Uptime |
| Skalierbarkeit | Niedrig | Hoch | Mittel |
| Drittanbieter | Keiner | Cloudflare (kostenlos) | Zapier (kostenl./bezahlt) |
Empfehlung nach Szenario
5. Feldmapping: HubSpot ↔ 4leads
Unabhaengig von der Variante werden folgende Felder synchronisiert
Kontaktdaten (Pflicht)
| HubSpot Property | Interner Name | → 4leads Feld | Richtung |
|---|---|---|---|
| HubSpot → 4leads | |||
| Vorname | firstname | Vorname | HubSpot → 4leads |
| Nachname | lastname | Nachname | HubSpot → 4leads |
| Firma | company | Firma / Tag | HubSpot → 4leads |
Segmentierungsdaten (fuer 4leads-Kampagnenzuordnung)
| HubSpot Property | Interner Name | → 4leads Feld | Nutzung in 4leads |
|---|---|---|---|
| Norm / Standard | norm_standard | Tags | Kampagnen-Zuordnung (FMEA, ISO 9001, etc.) |
| Dienstleistungs-Interesse | dienstleistung_interesse | Tags | Kampagnen-Zuordnung |
| Produkt-Interesse (Legacy) | produkt_interesse | Tags | Erweiterte Segmentierung |
| Sales-Status | sales_status | Custom Field | Kontakt-Klassifizierung |
| Kundenkategorie | kundenkategorie | Custom Field | VIP-Behandlung |
| Lead Score | lead_score_custom | Custom Field | Priorisierung |
Tracking-Daten (Sync-Management)
| HubSpot Property | Interner Name | Zweck |
|---|---|---|
| HubSpot Contact ID | hs_object_id | Mapping-Key |
| 4leads Kontakt-ID | fl_kontakt_id | Mapping-Key (Gegenrichtung) |
| 4leads Sync-Status | fl_sync_status | Pending / Synced / Error |
| In 4leads migriert | in_4leads_migriert | Ja / Nein (Trigger) |
Rueckkanal: 4leads → HubSpot (via Forms API)
| 4leads Event | → HubSpot Property | Wert |
|---|---|---|
| E-Mail geoeffnet | fl_interaktionstyp | Email Opened |
| Link geklickt | fl_interaktionstyp | Link Clicked |
| Abmeldung | fl_interaktionstyp | Unsubscribed |
| E-Mail gebounced | fl_interaktionstyp | Bounced |
| Kampagne abgeschlossen | fl_interaktionstyp | Campaign Completed |
| Interaktionsdatum | letzte_4leads_interaktion | ISO-Datum |
| Kampagnenname | fl_kampagnenname | Freitext |
6. Gesamtarchitektur
HubSpot (Sales Hub Pro)
4leads
7. Implementierungsreihenfolge
Phase 1: Sofort (unabhaengig von Variante) -- ~1.5 Stunden
Neue Properties anlegen (6 Stueck)
fl_interaktionstyp, fl_kampagnenname, letzte_4leads_interaktion, fl_kontakt_id, fl_sync_status + Property-Gruppe
Property-Gruppe erstellen
'4leads Integration' als neue Gruppe in HubSpot
System-Formular erstellen
[SYSTEM] 4leads Webhook Empfaenger -- unsichtbar, nur als API-Endpunkt
WF-4L-1: Interaktion verarbeiten
Trigger auf Formular-Einreichung, If/Then Branch nach Interaktionstyp, Score-Anpassung
WF-4L-2: Reaktivierung → Sales
Kalte Kontakte mit Link-Klick reaktivieren, Aufgabe + Notification fuer Tim
WF-4L-3: Unsubscribe verarbeiten
4leads-Markierung entfernen, ggf. Status auf Disqualifiziert
Aktive Liste erstellen
[NEU] 4leads Sync Queue -- Filter: in_4leads_migriert = Ja UND fl_sync_status != Synced
4leads Webhook-Konfiguration
5 Webhook-Events an HubSpot Forms API URL konfigurieren
Phase 2: HubSpot → 4leads (je nach Variante)
Phase 3: Testen
| Test | Wie |
|---|---|
| 4leads → HubSpot | Test-E-Mail in 4leads oeffnen, pruefen ob HubSpot-Property aktualisiert wird |
| HubSpot → 4leads | Kontakt auf in_4leads_migriert = Ja setzen, pruefen ob in 4leads ankommt |
| Unsubscribe | In 4leads abmelden, pruefen ob HubSpot-Status aktualisiert wird |
| Reaktivierung | Kalten Kontakt in 4leads klicken lassen, pruefen ob Sales-Aufgabe erstellt wird |
8. Was in 4leads konfiguriert werden muss
Komplette Anleitung fuer die 4leads-Seite der Integration
8.1 Uebersicht: Setup-Schritte
| # | Schritt | Wer | Aufwand |
|---|---|---|---|
| 1 | Webhook-Ausgang konfigurieren (5 Events → HubSpot) | QMH / 4leads-Admin | 15 Min |
| 2 | Webhook-Eingang konfigurieren (Kontakte empfangen) | QMH / 4leads-Admin | 10 Min |
| 3 | Custom Fields anlegen (falls nicht vorhanden) | QMH / 4leads-Admin | 10 Min |
| 4 | Kampagnen-Zuordnung ueber Tags pruefen | QMH | 10 Min |
| 5 | Testlauf durchfuehren | QMH + Prozessfaktor | 15 Min |
8.2 Webhook-Ausgang: Ziel-URL (fuer ALLE Events identisch)
Webhook-Payloads fuer alle 5 Events
Event 1: E-Mail geoeffnet
{ "fields": [
{ "name": "email", "value": "{{kontakt_email}}" },
{ "name": "fl_interaktionstyp", "value": "email_opened" },
{ "name": "fl_kampagnenname", "value": "{{kampagnen_name}}" },
{ "name": "fl_kontakt_id", "value": "{{kontakt_id}}" }
],
"context": {
"pageUri": "https://4leads.de/webhook",
"pageName": "4leads Email Opened"
}}Lead Score +5, fl_sync_status -> Synced
Event 2: Link geklickt
{ "fields": [
{ "name": "email", "value": "{{kontakt_email}}" },
{ "name": "fl_interaktionstyp", "value": "link_clicked" },
{ "name": "fl_kampagnenname", "value": "{{kampagnen_name}}" },
{ "name": "fl_kontakt_id", "value": "{{kontakt_id}}" }
],
"context": {
"pageUri": "https://4leads.de/webhook",
"pageName": "4leads Link Clicked"
}}Lead Score +15. Falls Status Kalt: Reaktivierung -> Opening Lead, Aufgabe fuer Tim
Event 3: Abmeldung (Unsubscribe)
{ "fields": [
{ "name": "email", "value": "{{kontakt_email}}" },
{ "name": "fl_interaktionstyp", "value": "unsubscribed" },
{ "name": "fl_kampagnenname", "value": "{{kampagnen_name}}" },
{ "name": "fl_kontakt_id", "value": "{{kontakt_id}}" }
],
"context": {
"pageUri": "https://4leads.de/webhook",
"pageName": "4leads Unsubscribed"
}}in_4leads_migriert -> Nein, Kontakt erhaelt KEINE weiteren 4leads-E-Mails
Event 4: E-Mail gebounced
{ "fields": [
{ "name": "email", "value": "{{kontakt_email}}" },
{ "name": "fl_interaktionstyp", "value": "bounced" },
{ "name": "fl_kampagnenname", "value": "{{kampagnen_name}}" },
{ "name": "fl_kontakt_id", "value": "{{kontakt_id}}" }
],
"context": {
"pageUri": "https://4leads.de/webhook",
"pageName": "4leads Bounced"
}}Kein Score-Impact. Sichtbar in Kontakt-Sidebar unter 4leads Integration
Event 5: Kampagne abgeschlossen
{ "fields": [
{ "name": "email", "value": "{{kontakt_email}}" },
{ "name": "fl_interaktionstyp", "value": "campaign_completed" },
{ "name": "fl_kampagnenname", "value": "{{kampagnen_name}}" },
{ "name": "fl_kontakt_id", "value": "{{kontakt_id}}" }
],
"context": {
"pageUri": "https://4leads.de/webhook",
"pageName": "4leads Campaign Completed"
}}Kein Score-Impact. Info fuer Tim: Kontakt hat komplette Serie durchlaufen
8.4 Platzhalter-Variablen in 4leads
| Platzhalter | Bedeutung | 4leads-Variable (typisch) |
|---|---|---|
| {{kontakt_email}} | E-Mail-Adresse des Kontakts | {email} oder {subscriber.email} |
| {{kampagnen_name}} | Name der aktuellen Kampagne/Serie | {campaign.name} oder {sequence.name} |
| {{kontakt_id}} | Interne 4leads-ID des Kontakts | {subscriber.id} oder {contact.id} |
Die genauen Variablennamen haengen von der 4leads-Version ab. Bitte in der 4leads-Webhook-Dokumentation nachschlagen.
8.5 Webhook-Eingang: HubSpot → 4leads (Kontakte empfangen)
Falls Variante B oder C gewaehlt wird. In 4leads: Einstellungen → Webhooks → Incoming.
Erwartetes Payload-Format:
{ "email": "max.mustermann@firma.de", "firstname": "Max", "lastname": "Mustermann", "company": "Muster GmbH", "tags": ["ISO 9001", "FMEA", "Schulung"], "custom_fields": { "hubspot_id": "12345678", "kundenkategorie": "Stammkunde", "lead_score": "45", "sales_status": "kalt" } }
Feldmapping in 4leads:
| Feld im Webhook | → 4leads Feld | Zweck |
|---|---|---|
| E-Mail (Pflicht) | Kontakt-Identifikation | |
| firstname | Vorname | Personalisierung |
| lastname | Nachname | Personalisierung |
| company | Firma oder Custom Field | Segmentierung |
| tags | Tags/Labels | Kampagnen-Zuordnung (entscheidend) |
| custom_fields.hubspot_id | Custom Field | Rueck-Referenz zu HubSpot |
| custom_fields.kundenkategorie | Custom Field | VIP-Erkennung |
| custom_fields.lead_score | Custom Field | Priorisierung in 4leads |
| custom_fields.sales_status | Custom Field | Aktueller Vertriebsstatus |
Tags → Kampagnen-Zuordnung in 4leads
| Tag (aus HubSpot) | → 4leads Kampagne |
|---|---|
| ISO 9001, Einfuehrung QM-System, Internes Audit | ISO 9001-Serie (10 E-Mails) |
| FMEA, FMEA-Moderation, FMEA-Schulung, FMEA-Review | FMEA-Serie (10 E-Mails) |
| Lieferantenentwicklung, Lieferantenaudit | Lieferantenentwicklung-Serie |
| IATF 16949, QMB-Uebernahme, Taskforce | Grossprojekte-Serie |
Bei mehreren Tags: In die Kampagne mit dem hoechsten Prioritaets-Tag einordnen, oder in mehrere Kampagnen parallel.
8.7 Checkliste: 4leads Setup
9. Offene Fragen (vor Implementierung)
| # | Frage | Antwort noetig von | Status |
|---|---|---|---|
| 1 | Wie heisst die 4leads Webhook-Eingangs-URL? | QMH / 4leads-Admin | OFFEN |
| 2 | Welche 4leads-Variablennamen fuer E-Mail, Kampagne, Kontakt-ID? | QMH / 4leads-Admin | OFFEN |
| 3 | Kann 4leads Kontakte per E-Mail matchen oder braucht es eine ID? | QMH / 4leads-Admin | OFFEN |
| 4 | Welche Variante (A/B/C) fuer HubSpot → 4leads? | QMH | OFFEN |
| 5 | Soll bei mehreren Tags der Kontakt in mehrere Kampagnen oder nur eine? | QMH | OFFEN |
| 6 | Gibt es in 4leads bereits Custom Fields oder muessen alle neu angelegt werden? | QMH / 4leads-Admin | OFFEN |
| 7 | Soll der Sync auch Updates umfassen (geaenderter Sales-Status → 4leads)? | QMH | OFFEN |