Diil Docs
  1. Doku
  2. Das Widget

Leads: Website-Formulare direkt ins CRM

Aktualisiert:

Ein Kontaktformular, das an jemanden im Urlaub mailt, ist der Ort, an dem Leads sterben. Mit Diil landet jedes Formular deiner Website als Karte im CRM — mit Telefonnummer oder E-Mail, einem Status und jemandem, der zurückruft. Ein Attribut am <form> reicht; JavaScript- und Server-Varianten sind da, wenn du mehr Kontrolle brauchst.

Drei Wege, einen Lead zu senden — nimm, was passt:

  • Ein Formular mit data-crm-lead — null JavaScript. Das Widget fängt den Submit ab und erledigt den Rest.
  • window.crmLead() — für React, Vue und jedes Formular, das du selbst steuerst.
  • POST /marketing/lead von deinem Server — telefonische Bestellungen, Backend-Formulare, Integrationen mit anderen Systemen.

Wie ein Lead vom Formular ins CRM kommt

In Diil ist ein Lead eine besondere Art von Event. Jedes Event hat einen Namen — etwa contact_form —, und dieser Name muss zuerst im CRM angelegt sein. Setz dort den Haken „Das ist ein Lead“, und jede Einsendung mit diesem Namen wird zur Karte unter CRM → Leads.

  1. Ein Besucher füllt das Formular aus und drückt „Senden“.
  2. Das Widget sammelt die Felder ein, sortiert, wer die Person ist (Name, Telefon, E-Mail, Nachricht), und schickt alles ab.
  3. Der Server prüft, ob der Name angelegt ist, ob der Lead eine Telefonnummer oder E-Mail hat und nicht nach Bot aussieht.
  4. Im CRM erscheint eine Lead-Karte — und, falls eingerichtet, eine Benachrichtigung in Telegram.

Zwei Regeln, die du dir von Anfang an merken solltest:

  • Ein nicht angelegter Name wird abgelehnt. Das CRM nimmt nur, was es kennt — kein überraschender Müll durch Tippfehler.
  • Angelegt, aber nicht als Lead markiert? Dann wird es als normales Event gespeichert, und es entsteht keine Lead-Karte.

Lead-Typ im CRM einrichten

Zwei Minuten Klicken, einmal pro Formulartyp:

  1. Marketing → Ereignisse öffnen

    Hier wird jedes Event beschrieben, das deine Site senden darf.
  2. Ein Event anlegen

    Gib ihm den Namen, den dein Code verwenden wird: lateinische Buchstaben, Ziffern und Unterstriche, beginnend mit einem Buchstaben — contact_form, callback_request, quote_request.
  3. Haken bei „Das ist ein Lead“ setzen

    Ab jetzt werden Einsendungen zu Lead-Karten. Wie der Hinweis im CRM sagt: Das Formular muss eine Antwortmöglichkeit mitschicken — Telefonnummer oder E-Mail.
  4. Optional: Haken bei „Dieses Ereignis bringt Umsatz“

    Nur dann werden Betrag und Währung des Leads gespeichert. Ohne den Haken werden sie stillschweigend ignoriert.

Variante A: ein Formular mit einem Attribut

Setz data-crm-lead="dein_event_name" an ein beliebiges Formular auf einer Seite mit dem Widget-Script. Das ist die ganze Integration. Formulare, die später auftauchen — in einem Modal, nach einem Routenwechsel in einer Single-Page-App —, werden automatisch erkannt.

Komplettes Beispiel

Ein „Angebot anfordern“-Formular mit Name, Telefon, E-Mail und Nachricht, einem Betrag für den Umsatz-Report, einer Danke-Nachricht und einer eigenen Prüfung. Erst reines HTML; die React- und Vue-Versionen rufen crmLead() aus ihrem eigenen Submit-Handler auf.

<form id="quote" data-crm-lead="quote_request" data-crm-value="149900" data-crm-currency="EUR">
  <label>Dein Name <input name="name" autocomplete="name" required></label>
  <label>Telefon <input name="phone" type="tel" autocomplete="tel"></label>
  <label>E-Mail <input name="email" type="email" autocomplete="email"></label>
  <label>Was brauchst du? <textarea name="message" rows="4"></textarea></label>

  <!-- Keins der bekannten Felder: erscheint so, wie es ist, in der Lead-Karte -->
  <label>Teamgröße
    <select name="team_size">
      <option>1–5</option>
      <option>6–20</option>
      <option>20+</option>
    </select>
  </label>

  <button type="submit">Anfrage senden</button>
  <p class="form-status" role="status" hidden></p>
</form>

<script>
  const form = document.getElementById('quote');
  const status = form.querySelector('.form-status');
  const button = form.querySelector('button');

  function say(text) {
    status.textContent = text;
    status.hidden = false;
  }

  // 1. Vor dem Senden: unsere eigene Prüfung. preventDefault() = nichts wird gesendet
  form.addEventListener('crm:lead-before', (event) => {
    const phone = form.elements.phone.value.trim();
    const email = form.elements.email.value.trim();
    if (!phone && !email) {
      event.preventDefault();
      say('Bitte hinterlass eine Telefonnummer oder E-Mail, damit wir antworten können.');
      return;
    }
    button.disabled = true;
  });

  // 2. Nach dem Senden: Das Widget zeigt nichts an, das „Danke“ kommt von uns
  form.addEventListener('crm:lead', (event) => {
    button.disabled = false;
    say(event.detail.ok
      ? 'Danke! Wir melden uns innerhalb eines Werktags.'
      : 'Da ist etwas schiefgelaufen. Versuch es bitte noch mal oder ruf uns an.');
  });
</script>

<script src="https://widget.sitecog.com/widget.js" defer></script>

Was beim Absenden des Formulars passiert

  1. Das Widget bricht den nativen Submit immer ab — die Seite lädt nicht neu, und die action des Formulars wird nicht genutzt.
  2. Es feuert crm:lead-before am Formular. Ruft irgendein Listener event.preventDefault() auf, ist die Geschichte hier zu Ende, und nichts wird gesendet.
  3. Es sammelt die Felder mit FormData ein — außer den ausgeschlossenen. Es zählen nur Textwerte: Datei-Inputs werden ignoriert. Mehrere Felder mit demselben Namen (eine Gruppe Checkboxen) werden mit ", " verbunden.
  4. Es sendet den Lead, geschützt durch einen einmaligen Formular-Pass (mehr dazu unter Spamschutz).
  5. Es feuert crm:lead mit { ok: true } oder { ok: false } in event.detail.
  6. Ist ok true, ruft es form.reset() auf. Bei einem Fehler bleibt das Formular, wie es ist, damit der Besucher nicht verliert, was er getippt hat.

Das Widget zeigt keine eigene UI — keinen Toast, keinen Spinner, kein „Danke“. Deine Site, dein Design, deine Worte: Hör auf crm:lead und zeig, was du willst.

Formular-Attribute

AttributBeispielWas es tut
data-crm-lead"quote_request"Macht das Formular zum Lead-Formular. Der Wert ist der im CRM angelegte Event-Name. Ein leeres Attribut bedeutet den Namen lead_submit — der muss ebenfalls angelegt sein.
data-crm-value"149900"Betrag in kleinsten Einheiten, eine Ganzzahl: 149900 sind 1.499,00. Wird nur gespeichert, wenn beim Event „Dieses Ereignis bringt Umsatz“ gesetzt ist.
data-crm-currency"EUR"Währung des Betrags: EUR, USD, UAH…
data-crm-ignore(ohne Wert)Gehört nicht ans Formular, sondern an ein Feld oder einen Wrapper (<fieldset>, <div>…): Das Feld bzw. alles darin wird nie gesendet. Siehe Felder, die nie gesendet werden.

Formular-Events

Beide Events bubbeln, du kannst also am Formular selbst lauschen oder einmal an document für alle Formulare der Seite.

EventWannevent.detailAbbrechbar
crm:lead-beforeDirekt nach dem Submit, bevor irgendetwas gesendet wird{ name } — der Event-NameJa: preventDefault() stoppt das Senden
crm:leadNachdem der Server geantwortet hat{ ok } — true, wenn der Lead angenommen wurdeNein
Ein Listener für alle Lead-Formulare der Seitejs
document.addEventListener('crm:lead', (event) => {
  if (event.detail.ok) {
    event.target.closest('.modal')?.classList.add('is-thanks');
  }
});

Feldnamen, die das CRM versteht

Eine Lead-Karte hat vier Hauptfelder: Name, Telefon, E-Mail und Nachricht. Das Widget füllt sie anhand der Feldnamen. Die Namen werden getrimmt und kleingeschrieben und dann exakt mit dieser Liste verglichen (Phone funktioniert also, your-phone nicht):

FeldFeldnamen, die es füllenMax. Länge
Namename, fio, username, user_name, fullname, full_name, firstname, first_name, contact_name, client_name, имя, фио, ім'я200
Telefonphone, tel, telephone, mobile, phone_number, contact_phone, телефон, тел40
E-Mailemail, e_mail, e-mail, mail, contact_email, почта, пошта, емейл320
Nachrichtmessage, comment, comments, text, question, note, description, task, сообщение, комментарий, вопрос, повідомлення, коментар4000
  • Der erste Treffer gewinnt. Hat ein Formular sowohl phone als auch mobile, füllt das erste im Formular das Feld.
  • Alles andere bleibt auch erhalten. Unbekannte Felder und zweite Treffer landen in „Zusätzlich“ und erscheinen in der Lead-Karte in Formular-Reihenfolge. Bis zu 30 Zusatzfelder; Keys bis 60 Zeichen, Werte bis 1000.
  • Eine Telefonnummer zählt nur mit 7 bis 20 Ziffern. Leerzeichen, Klammern und Bindestriche sind okay; ein führendes + bleibt erhalten.
  • Telefon oder E-Mail ist Pflicht. Ein Lead ohne beides wird mit no_contact abgelehnt — es gäbe keinen Weg, zu antworten.

Felder, die nie gesendet werden

Manche Daten haben in einem CRM nichts verloren — Passwörter, Kartennummern, CSRF-Tokens. Das Widget lässt sie weg, bevor irgendetwas das Formular verlässt:

  • Passwortfelder — jedes <input type="password">.
  • Alles mit data-crm-ignore — am Feld selbst oder an einem beliebigen Wrapper (<fieldset>, <div>…). Dann wird alles darin übersprungen.
  • Felder mit sensiblem Namen. Als eigenes Wort im Namen (getrennt durch _, -, . oder camelCase): card, cc, csc, cvv, cvc, iban, ssn, secret, pass. Irgendwo im Namen: password, passwd, pwd, token, csrf, xsrf, creditcard, cardnumber, ccnum.

Ein paar Beispiele: card_number ✗, cvv ✗, csrf_token ✗, user_password ✗ — aber discard_reason ✓, denn dort ist „card“ kein eigenes Wort.

  • Ein Name, ein Urteil. Ist ein Feld mit einem bestimmten Namen ausgeschlossen, sind es alle Felder mit diesem Namen.
  • Versteckte Inputs werden gesendet — praktisch für Produkt, Tarif oder Seite. Es sei denn, ihr Name ist sensibel.
Was ankommt und was nichthtml
<form data-crm-lead="signup_request">
  <input name="email" type="email">               <!-- ✓ gesendet -->
  <input name="password" type="password">         <!-- ✗ Passwortfeld -->
  <input type="hidden" name="plan" value="pro">   <!-- ✓ versteckt, aber gesendet -->

  <fieldset data-crm-ignore>                      <!-- ✗ alles hier drin -->
    <input name="billing_address">
    <input name="vat_id">
  </fieldset>

  <input name="card_number">                      <!-- ✗ „card“ als eigenes Wort -->
  <input name="discard_reason">                   <!-- ✓ hier ist „card“ kein eigenes Wort -->

  <button type="submit">Registrieren</button>
</form>

Dieselbe Namensregel gilt für die Keys von crmLead(). Und der Server filtert genauso — auch bei POST /lead mit geheimem Schlüssel. Ein sensibles Feld landet also nie im CRM, egal auf welchem Weg es kommt.

Variante B: Leads aus JavaScript senden

Wenn du den Formular-State selbst hältst — React, Vue, ein mehrstufiger Wizard, ein Chatbot —, ruf das Widget direkt auf. Beide Funktionen erscheinen auf window, sobald widget.js geladen ist.

crmLead(name, fields, options)

const { ok } = await window.crmLead(
  'callback_request',
  { name: 'Anna', phone: '+49 30 1234567', message: 'Bitte ruf mich nach 17 Uhr an' },
  { value: 149900, currency: 'EUR' },
);
namestringPflicht
Im CRM angelegter Event-Name, markiert mit „Das ist ein Lead“. Lateinische Buchstaben, Ziffern und Unterstriche, beginnend mit einem Buchstaben (^[A-Za-z][A-Za-z0-9_]*$). Ein Name, der nicht passt, bringt eine Warnung in der Konsole und { ok: false }.
fieldsobjectPflicht
Die Formulardaten als einfache Key-Value-Paare. Für die Keys gelten dieselben Feldnamen-Regeln wie bei Formularen: Bekannte füllen Name, Telefon, E-Mail und Nachricht, der Rest geht nach „Zusätzlich“. Keys mit sensiblem Namen werden genauso weggelassen wie im Formular.
options.valueintegeroptional
Betrag in kleinsten Einheiten: 149900 = 1.499,00. Wird nur bei Events mit „Dieses Ereignis bringt Umsatz“ gespeichert.
options.currencystringoptional
Währung des Betrags, z. B. EUR.

Gibt ein Promise zurück, das aufgelöst wird zu:

okboolean
true — der Lead ist angenommen. false — er wurde abgelehnt oder ist nicht durchgekommen; den Grund verraten wir absichtlich nicht (siehe warum).

crmLeadForm(form, name)

Macht genau das, was das Attribut data-crm-lead macht, nur aus dem Code. Praktisch, wenn das Formular von einer Fremdbibliothek gerendert wird und du keine Attribute ergänzen kannst, oder wenn der Name erst zur Laufzeit feststeht. Gibt nichts zurück; Ergebnisse kommen über dasselbe crm:lead-Event.

const form = document.querySelector('#newsletter-popup form');
window.crmLeadForm(form, 'newsletter_signup');

form.addEventListener('crm:lead', (e) => {
  if (e.detail.ok) form.innerHTML = '<p>Du bist dabei! Schau in dein Postfach.</p>';
});
formHTMLFormElementPflicht
Das Formular-Element, das verbunden werden soll.
namestringPflicht
Im CRM angelegter Event-Name, gleiche Regeln wie bei crmLead.

TypeScript-Deklarationen

Das Widget ist ein einfaches Script, TypeScript weiß also nichts davon. Pack das in eine beliebige .d.ts-Datei:

crm-widget.d.tsts
export {};

declare global {
  interface Window {
    crmLead?: (
      name: string,
      fields: Record<string, string>,
      options?: { value?: number; currency?: string },
    ) => Promise<{ ok: boolean }>;
    crmLeadForm?: (form: HTMLFormElement, name: string) => void;
  }
}

Wie der Spamschutz funktioniert

Ein öffentliches Formular zieht Bots an wie ein Magnet. Ein CAPTCHA brauchst du trotzdem nicht — Widget und Server erledigen das gemeinsam, und echte Besucher merken nichts davon.

Ein einmaliger Formular-Pass

Vor dem Senden holt sich das Widget einen signierten „Formular-Pass“ vom Server. Um Zeit zu sparen, fragt es danach, sobald der Besucher das Formular zum ersten Mal fokussiert. Ein Pass ist:

  • einmalig — ein Pass, ein Lead;
  • an deine Site gebunden — ein Pass von einer Site ist auf einer anderen nutzlos;
  • 24 Stunden gültig.

Bots, die blind an den Endpoint POSTen, haben keinen Pass und werden abgelehnt. Und ein Doppelklick oder erneutes Absenden mit demselben Pass liefert { ok: true }, der Lead wird aber nur einmal gespeichert — keine doppelten Karten durch ungeduldige Finger.

Der Honeypot

Das versteckte Feld company_site von oben. Ein Mensch sieht es nicht, also bleibt es leer. Ein Bot, der jedes Feld ausfüllt, das er findet, verrät sich selbst, und der Lead wird abgelehnt.

„Verdächtige“ Leads

Manche Leads sehen seltsam aus, könnten aber echt sein. Die werden angenommen und bekommen im CRM das Label Verdächtig, damit ein Manager vor dem Anruf kurz draufschauen kann:

  • das Formular wurde in unter 3 Sekunden ausgefüllt — schneller als menschenmöglich;
  • das Formular war vor dem Absenden länger als 30 Minuten offen;
  • mehr als 5 Leads kamen innerhalb einer Stunde von derselben IP-Adresse.

Die IP-Adresse, die du in der Lead-Karte siehst, ist übrigens gekürzt: Bei IPv4 ist das letzte Oktett genullt, IPv6 ist auf /48 abgeschnitten — dieselbe Regel wie unter Was wir speichern und wie lange.

„Spam“ ist ein Status, den ein Mensch im CRM von Hand setzt. Nichts wird automatisch als Spam markiert, also fliegt kein echter Kunde stillschweigend raus.

Warum der Browser nur ok: true oder false bekommt

Einem Bot „abgelehnt: Honeypot ausgefüllt“ zu sagen, ist eine Gratis-Lektion, wie man durchkommt. Deshalb sieht aus dem Browser jede Ablehnung gleich aus — { ok: false }, ohne Grund. Du debuggst dein eigenes Formular? Die Checkliste zur Fehlersuche deckt jeden Fall ab, und die Server-Route sagt dir sehr wohl, was schiefging, weil sie durch einen geheimen Schlüssel geschützt ist.

Unter der Haube: der Browser-Request

Für Neugierige — selbst bauen musst du das nie. Das Widget holt sich einen Pass über POST https://back.sitecog.com/marketing/fk (Antwort: {"pass":"…"}) und sendet dann den Lead:

Was das Widget sendethttp
POST https://back.sitecog.com/marketing/f
Content-Type: application/json

{
  "n": "contact_form",
  "pass": "…",
  "fields": {
    "name": "Anna",
    "email": "anna@example.com",
    "message": "Hi!",
    "company_site": ""
  },
  "val": 149900,
  "cur": "EUR",
  "u": "https://shop.example/contacts",
  "vid": "…",
  "sid": "…"
}

Die Antwort ist { "ok": true } oder { "ok": false }. vid und sid sind die IDs von Besucher und Session, damit der Lead mit dem Werbekanal verknüpft wird, über den der Besucher kam — siehe Events & Analytics.

Leads von deinem Server senden

Nicht jeder Lead beginnt im Browser. Nimm die Server-Route, wenn:

  • ein Manager eine Bestellung am Telefon annimmt und in dein Backoffice eintippt;
  • dein Formular im Backend verarbeitet wird (ein PHP-Handler, eine Next.js Server Action) und du nicht vom Widget abhängen willst;
  • Leads aus einem anderen System kommen: einem Marktplatz, einem Buchungsservice, einem Bot.
POST https://back.sitecog.com/marketing/lead
x-event-key: sk_…
Content-Type: application/json

Geheime Schlüssel

Die Server-Route authentifiziert sich mit einem geheimen Schlüssel. Leg einen an unter CRM → Marketing → Ereignisse → Geheime Schlüssel → Schlüssel ausstellen. Ein Schlüssel sieht aus wie sk_ + 48 Hex-Zeichen; eine Site kann bis zu 5 aktive Schlüssel haben, und jeder davon lässt sich widerrufen. Gib jedem Schlüssel einen Namen, der sagt, wo er lebt („Payment-Server“, „Telefonbestellungen“) — dein zukünftiges Ich wird es dir danken, wenn einer widerrufen werden muss.

Beispiel-Request

curl https://back.sitecog.com/marketing/lead \
  -H "x-event-key: sk_3f9a1c7e5b2d4f6a8c0e1b3d5f7a9c2e4b6d8f0a1c3e5b7d" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "phone_order",
    "event_id": "order-1024",
    "fields": {
      "name": "Anna Schmidt",
      "phone": "+49 30 1234567",
      "message": "Zwei Paar Air 3, Graphit. Lieferung ins Büro."
    },
    "value": 149900,
    "currency": "EUR",
    "url": "https://shop.example/contacts"
  }'

Request-Body

x-event-keyheaderstringPflicht
Geheimer Schlüssel sk_… aus dem CRM. Fehlt oder widerrufen → 401 invalid_key.
namestringPflicht
Im CRM angelegter Event-Name, markiert mit „Das ist ein Lead“. Kurzer Alias: n.
fieldsobjectPflicht
Der Lead selbst: Name, Telefon, E-Mail, Nachricht und alles andere, nach denselben Feldnamen-Regeln. Muss eine Telefonnummer oder E-Mail enthalten. Kontaktdaten immer hier verschachteln — Keys auf der obersten Ebene des Bodys werden in den Lead gemischt. Felder mit sensiblem Namen filtert der Server auch hier heraus — siehe Felder, die nie gesendet werden.
event_idstringoptional
Deine ID für diesen Lead, bis 128 Zeichen — eine Bestellnummer, eine Ticket-ID. Macht den Request idempotent. Alias: id.
visitor_idstringoptional
Die Besucher-ID aus dem Browser (crm_vid). Alias: vid. Siehe Attribution.
session_idstringoptional
Die Session-ID aus dem Browser (crm_sid). Damit erbt der Lead Werbekanal und UTM-Tags dieser Session. Alias: sid.
valueintegeroptional
Betrag in kleinsten Einheiten: 149900 = 1.499,00. Wird nur bei Events mit „Dieses Ereignis bringt Umsatz“ gespeichert. Alias: val.
currencystringoptional
Währung des Betrags, z. B. EUR. Alias: cur.
urlstringoptional
Die Seite, um die es beim Lead geht oder von der er kam. Alias: u.

Response

200 OKjson
{ "ok": true, "duplicate": false }
okboolean
Bei einer 200 immer true. Probleme kommen als 4xx mit einer message zurück — siehe Fehler.
duplicateboolean
true, wenn es einen Lead mit dieser event_id schon gibt. Nichts Neues wurde gespeichert, und das ist in Ordnung.

Anders als der Browser nutzt die Server-Route weder Formular-Pässe noch Zeitprüfungen — der geheime Schlüssel ist Beweis genug. Der Honeypot gilt trotzdem: Ein nicht leeres company_site in fields bekommt 400 rejected.

Idempotenz: Retry ohne Angst

Netzwerke fallen im ungünstigsten Moment aus. Ist der Request durchgegangen oder nicht? Mit event_id musst du das nicht wissen: Schick ihn einfach noch mal. Eine Wiederholung liefert {"ok":true,"duplicate":true}, und der Lead wird nicht doppelt gespeichert.

Gleiche event_id, zweites Maljson
{ "ok": true, "duplicate": true }

Attribution: welche Anzeige den Lead gebracht hat

Leads aus dem Widget werden automatisch mit dem Besucher verknüpft. Ein Server-Lead weiß nichts über den Browser — es sei denn, du sagst es ihm. Das Widget legt die Besucher-ID in localStorage unter crm_vid ab und die Session-ID unter crm_sid. Schick sie zusammen mit dem Formular oder der Bestellung an dein Backend und reich sie weiter:

Im Browser, beim Checkoutjs
const crm = {
  vid: localStorage.getItem('crm_vid'),
  sid: localStorage.getItem('crm_sid'),
};

await fetch('/api/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ ...order, crm }),
});
// …und auf dem Server: visitor_id: body.crm.vid, session_id: body.crm.sid

Mit session_id erbt der Lead Werbekanal und UTM-Tags des ersten Seitenaufrufs dieser Session und taucht so in den Reports neben der Kampagne auf, die ihn verdient hat. Mehr dazu unter Events & Analytics.

Fehler

Aus dem Browser sagen crmLead() und das crm:lead-Event immer nur ok: false. Die Server-Route antwortet mit einem Status und einer message:

StatusBodyWas passiert ist
400{"message":"unknown_event","name":"phone_order"}Kein Event mit diesem Namen im CRM. Leg es unter Marketing → Ereignisse an (prüf Schreibweise und Groß-/Kleinschreibung).
400{"message":"invalid_event_name"}Der Name enthält Zeichen außer lateinischen Buchstaben, Ziffern und Unterstrichen oder beginnt nicht mit einem Buchstaben.
400{"message":"no_contact"}Weder Telefonnummer (7–20 Ziffern) noch E-Mail in fields.
400{"message":"invalid_body"}Der Body ist kein JSON-Objekt in der erwarteten Form.
400{"message":"rejected"}Das Honeypot-Feld company_site ist ausgefüllt.
401{"message":"invalid_key"}x-event-key fehlt, ist fehlerhaft oder widerrufen.
413—Der Body ist größer als 8 KB.
429{"message":"rate_limit_exceeded"}Zu viele Requests in dieser Minute. Warte auf die nächste.

Browser-Requests können außerdem 400 unknown_origin oder 400 unknown_domain bekommen, wenn die Domain der Seite keine Site im CRM ist — die siehst du im Network-Tab, während der Code weiterhin ok: false bekommt.

Limits

WasLimit
Requests pro IP (alle Marketing-Routen zusammen)120 pro Minute
Requests pro Site6000 pro Minute
Formular-Pässe pro IP20 pro Minute
Request-Body8 KB
Name / Telefon / E-Mail / Nachricht200 / 40 / 320 / 4000 Zeichen
Zusatzfelderbis zu 30; Key bis 60, Wert bis 1000 Zeichen
Telefon7–20 Ziffern, um als Kontakt zu zählen
event_idbis 128 Zeichen
Geheime Schlüsselbis zu 5 aktive pro Site

Die Zeitfenster sind feste Minuten: Nach einer 429 wartest du, bis die nächste Minute beginnt. Ein echter Besucher kommt nie in die Nähe; ein Script, das auf dein Formular einhämmert, schon.

Fehlersuche

Der Lead ist nicht angekommen

Geh die Liste durch — fast immer ist es einer dieser Punkte:

  • Der Name ist nicht angelegt. Unter Marketing → Ereignisse muss es ein Event mit genau diesem Namen geben, inklusive Groß-/Kleinschreibung. Ein leeres data-crm-lead bedeutet lead_submit — leg genau das an.
  • „Das ist ein Lead“ ist nicht angehakt. Dann wird die Einsendung als normales Event gespeichert: Such sie in der Event-Statistik, nicht unter Leads.
  • Kein Telefon und keine E-Mail. Oder sie sind da, aber unter Namen, die das CRM nicht kennt (your-phone, contact[email]), und sind in „Zusätzlich“ gelandet. Benenne sie um — siehe die Feldtabelle. Eine Telefonnummer mit weniger als 7 Ziffern zählt auch nicht.
  • Die Daten steckten in einem Datei-Input. Dateien werden ignoriert — das Widget sendet nur Text.
  • Ein echtes Feld heißt company_site. Das ist der Honeypot-Name; der Lead wird als Bot behandelt.
  • Dein eigener crm:lead-before-Listener hat preventDefault() aufgerufen — vielleicht nicht dann, wann du dachtest.
  • Das Widget ist nicht auf dieser Seite. Ohne widget.js tut das Attribut nichts, und das Formular wird auf die altmodische Art abgeschickt (oder gar nicht).
  • Eine Content Security Policy blockiert es. Hat deine Site eine CSP, braucht sie script-src https://widget.sitecog.com und connect-src https://back.sitecog.com. Die Browser-Konsole sagt es dir in Rot.
  • Die Domain ist keine Site im CRM. Die Site wird am Origin der Seite erkannt (www. wird entfernt). Ein Test auf localhost oder einer Staging-Domain, die nicht im CRM eingetragen ist, ergibt unknown_domain im Network-Tab.
  • Du hast viel getestet. 20 Formular-Pässe pro Minute und IP reichen für Menschen locker, aber nicht für hektisches Klicken. Warte eine Minute.

Ein Feld ist nicht angekommen

Der Lead ist da, aber ein Feld fehlt? Meist wurde es mit Absicht weggelassen:

  • Der Name klingt sensibel. Prüf ihn gegen die Regeln unter Felder, die nie gesendet werden: promo_pass etwa fliegt wegen pass raus. Benenn es um — promo_code kommt problemlos durch.
  • Irgendwo steht data-crm-ignore. Am Feld selbst oder an einem Wrapper weiter oben im DOM — dort gilt es für alles darin.
  • Ein anderes Feld mit demselben Namen ist ausgeschlossen. Dann sind es alle Felder mit diesem Namen.
  • Es ist ein Passwortfeld oder ein Datei-Input. Passwörter werden nie gesendet, und Dateien ignoriert das Widget sowieso.

Der Betrag wird nicht angezeigt

Setz beim Event-Typ den Haken „Dieses Ereignis bringt Umsatz“. Prüf außerdem, ob der Betrag eine Ganzzahl in kleinsten Einheiten ist: 1499.00 wird als 149900 gesendet.

Benachrichtigungen in Telegram

Ein Lead, den niemand sieht, ist ein verlorener Lead. In den Einstellungen unter CRM → Leads kannst du einen Telegram-Bot verbinden: Bot-Token eintragen, Benachrichtigungen einschalten, entscheiden, ob verdächtige Leads auch geschickt werden, und Ruhezeiten festlegen, damit die Nachtschicht der Bots dein Sales-Team nicht weckt.

Benachrichtigungen sind mit HTML formatiert, und alles, was der Besucher eingegeben hat, wird vorher escaped. Ein „Name“ wie <a href="…">Hier klicken</a> kommt als reiner Text an: Niemand kann deinem Team einen Link in den Chat schmuggeln oder die Formatierung zerschießen. Lange Felder werden gekürzt, damit ein Roman im Nachrichtenfeld nicht zur Textwand wird – der vollständige Lead steht immer im CRM.

Daten einer Person auf Anfrage löschen

„Bitte löscht alles, was ihr über mich habt“ ist eine ganz normale Bitte, und sie zu erfüllen sollte nicht eine Woche Wühlen in Tabellen bedeuten. Das CRM hat dafür zwei Werkzeuge – ohne Code auf deiner Seite.

Aus einem Lead oder Chat

Die Lead-Karte und die Chat-Karte haben beide „Daten des Besuchers löschen“. Bevor etwas gelöscht wird, zeigt das CRM genau, was alles verschwindet:

  • das Besucherprofil und Analytics – Seitenaufrufe und Events;
  • Chats und Anhänge – die Zahl der Chats, Nachrichten und Dateien;
  • Leads – nur, wenn du „Auch die Anfragen dieses Besuchers löschen“ ankreuzt. Standardmäßig aus: Ein Lead ist oft ein laufender Deal, und die Entscheidung liegt bei dir.

Ein Lead, den du von deinem Server ohne visitor_id gesendet hast, hängt an keinem Besucher – dort lässt sich nur der Lead selbst löschen.

Per E-Mail oder Telefon

Meist kommt die Bitte per E-Mail: „Ich bin anna@example.com, vergesst mich“. Geh zu CRM → Einstellungen → „Anfragen zur Datenlöschung“, gib die E-Mail oder Telefonnummer ein, die die Person angegeben hat, und das CRM findet ihre Leads, Chats und ihr Konto auf deiner Website. Ein Button löscht alles Gefundene; die Besuchs-Analytics der zugehörigen Besucher kann gleich mit weg.

  • Rechte. Löschen braucht dieselben Rechte wie das Bearbeiten dieser Bereiche. Ein Manager, der Chats nur ansehen darf, sieht die Treffer, kann sie aber nicht löschen.
  • Aktivitätsprotokoll. Jede Löschung landet im Aktivitätsprotokoll – ohne E-Mail und Telefonnummer der Person, sonst würde das Protokoll genau das aufbewahren, was vergessen werden sollte.
  • Kein Rückgängig. Gelöscht ist gelöscht. Prüf die Zahlen, bevor du bestätigst.