• Virtual Marketer CRM Logo
    Virtual Marketer CRMDokumentation
  • Einführung
Erste Schritte
  • Quickstart
  • Kernkonzepte
KI verbinden
  • Custom Connector
  • CLI & Editoren
  • Ratenlimits
Integrationen
  • MCP
  • Webhooks
  • Leads von der Website
  • OpenAPI 3.1.0
  • N8N
  • WhatsApp-API
App-Leitfaden
  • Dashboard
  • Posteingang
  • Datensätze
  • Profil
  • Unternehmen
  • Lead-Versand
  • API-Keys
  • Filter-Syntax
Lizenzen & Sicherheit
  • Architektur & Sicherheit
  • Zurück
  1. Einführung
  2. Leads von der Website

Leads von der Website

Ein Formular auf der eigenen Website legt Contacts im CRM an — welcher Endpoint, welcher Key, welche Felder, und was das CRM dabei nicht für Sie tut.

Ein Newsletter-Formular, ein Downloadgatter, ein Kontaktformular: Die Anmeldung soll als Contact in Ihrem Workspace landen. Das geht, und zwar über einen einzigen Aufruf — aber nicht über einen Webhook, und nicht mit Ihrem eigenen API-Key. Diese Seite beschreibt den Weg, den wir empfehlen, und nennt die drei Dinge, die es dafür bewusst nicht gibt.

Die Richtung stimmt anders, als man erwartet

Webhooks gehen aus dem CRM heraus. Sie abonnieren Events wie contact.created und das CRM ruft Ihren Endpoint. Es gibt keinen Endpoint, auf den ein Formular ohne Authentifizierung posten könnte — ein solcher Endpoint wäre eine offene Schreiboberfläche auf Ihren Datenbestand, und deshalb existiert er nicht.

Ihr Server ruft also das CRM, nicht umgekehrt: POST /api/v1/contacts mit dem Header x-api-key. Das Formular selbst ruft das CRM nie direkt — der Key gehört auf Ihren Server und darf nicht im Browser landen.

Schritt 1: ein eigener Benutzer für das Formular

Nehmen Sie nicht Ihren persönlichen Key. Ein API-Key erbt die Rechte seines Benutzers, und eigene Rechte pro Key gibt es nicht. Ein Key von Ihrem Account kann alles, was Sie können — auf einem Webserver ist das mehr Vertrauen, als ein Anmeldeformular braucht.

Legen Sie stattdessen unter Unternehmen → Rollen eine eigene Rolle an, laden Sie einen Benutzer dafür ein und erzeugen Sie den Key als dieser Benutzer.

Wie eng die Rolle werden kann, sollten Sie vorher wissen, denn eine Grenze überrascht.

Der Rolleneditor vergibt Anlegen, Bearbeiten und Löschen pro Bereich einzeln, dazu die Leserechte (keine / eigene / alle). Eine Rolle, die nur anlegen darf, ist damit ein einzelnes Häkchen.

Und Leserechte keine funktioniert nicht. Wer nicht alles lesen darf, muss jeden Datensatz, den er anlegt, sich selbst zuweisen — sonst entstünde ein Datensatz, den sein Urheber nicht mehr sehen kann. Das Anlegen scheitert dann mit 400 und „Sie müssen sich selbst zuweisen“ am Feld userIds. Für ein Formular ist das die falsche Richtung: Der Lead soll dem Vertrieb gehören, nicht dem Webserver.

Die engste Rolle, die ein Formular tragen kann, ist damit:

  • Contacts: Anlegen ja, Bearbeiten nein, Löschen nein, Leserechte alle.
  • Alles andere aus.

Danach: Profil → API & Konnektoren → Hinzufügen, als dieser Benutzer. Der Key wird einmal angezeigt, gehört in eine Umgebungsvariable Ihres Servers und nicht ins Repository. Details unter API-Keys.

Was das im Zwischenfall wert ist, und was nicht: Ein so beschnittener Key sieht keine Deals, Organisationen, Rechnungen oder Postfächer, und Sie können ihn löschen, ohne dass sonst etwas stehenbleibt. Er liest aber alle Contacts und kann sie auch ändern und löschen, weil der Editor das nicht trennt — Ihr gesamter Kontaktbestand hängt also am Schlüssel auf Ihrem Webserver. Wenn Ihnen das zu viel ist, gehört der Schreibweg hinter einen eigenen kleinen Dienst auf Ihrer Seite, der nur Anlegen durchlässt.

Schritt 2: den Contact anlegen

curl -X POST https://crm.virtual-marketer.de/api/v1/contacts \
     -H "x-api-key: $VM_CRM_API_KEY" \
     -H "Content-Type: application/json" \
     -d '{
       "firstName": "Jana",
       "lastName": "Beck",
       "notes": "Media-Kit angefordert am 17.09.2026, Formular /media-kit",
       "identifiers": [
         { "provider": "mail", "value": "jana.beck@example.de" }
       ]
     }'

Antwort 201 mit dem angelegten Contact, 400 mit einem lesbaren Validierungsfehler.

Was dabei zu wissen ist:

  • firstName darf nicht leer sein, lastName muss vorhanden sein und darf leer sein. Ein Formular, das nur ein Feld „Name“ hat, schreibt den ganzen Namen in firstName und "" in lastName — das ist gültig und ehrlicher, als einen Nachnamen zu raten.
  • E-Mail-Adresse und Telefonnummer sind keine Felder am Contact, sondern Kanäle. Sie stehen in identifiers als { provider, value }, mit provider aus mail, google, outlook, whatsapp. Das ist nicht nur eine andere Schreibweise: Schreibt dieselbe Person später eine Mail oder eine WhatsApp-Nachricht an ein verbundenes Postfach, ordnet der Posteingang sie automatisch genau diesem Contact zu. Eine Adresse, die nur in notes steht, tut das nicht.
  • Ein Wert, der sich nicht normalisieren lässt, ist ein 400, und dann wird gar nichts angelegt. Prüfen Sie das Format auf Ihrer Seite, bevor Sie senden, sonst verlieren Sie die Anmeldung vollständig.
  • Eigene Spalten setzen Sie über customFieldValues als { columnId, value }. Die IDs stehen in der Antwort von GET /api/v1/contacts/configuration; bei einer Auswahl-Spalte ist value die UUID der Option, nicht ihre Beschriftung. Auswahl-Spalten mit einer Standard-Option füllt das CRM beim Anlegen von selbst — ein Lead bekommt also die erste Vertriebsphase, ohne dass Ihr Formular sie mitschickt.

Schritt 3: dieselbe Adresse zweimal

Das CRM dedupliziert selbst, aber nur über den Kanal: Eine E-Mail-Adresse oder eine WhatsApp-Nummer kann immer nur an einem Contact hängen. Ein zweiter POST mit einer bereits verknüpften Adresse ist deshalb ein 400:

✖ This channel is already linked to another contact.
  → at identifiers[0].value

Das ist kein Ausfall, sondern die Antwort „kennen wir schon“. Behandeln Sie genau diesen Fall getrennt: nicht wiederholen, nicht als Störung melden — die Person steht bereits im CRM. Jeder andere 400 ist ein echter Fehler in Ihren Daten; ein 5xx oder ein Timeout gehört wiederholt.

Was daraus folgt:

  • Eine Vorabsuche brauchen Sie für die Datenqualität nicht. Sinnvoll ist sie nur, wenn Ihre Seite dem Absender etwas anderes antworten will („Sie sind schon eingetragen“): POST /api/v1/contacts/search mit { "searchTerm": "jana.beck@example.de" }.
  • Die zweite Anmeldung wird nicht übernommen. Schickt dieselbe Person das Formular noch einmal, mit neuer Telefonnummer oder einem anderen Anliegen, entsteht nichts und es ändert sich nichts. Halten Sie das bei sich fest, oder lassen Sie einen zweiten, änderungsberechtigten Benutzer nacharbeiten — bewusst nicht derselbe Key wie der auf dem Webserver.
  • Dedupliziert wird über den Kanal, nicht über den Namen. Dieselbe Person mit zwei Adressen wird zu zwei Contacts.

Was das CRM dabei nicht tut

  • Es verschickt keine Benachrichtigungsmail, wenn ein Datensatz entsteht. Die Mail „neuer Lead“ schickt Ihr eigener Server im selben Request — er hat die Daten ohnehin. Wer sie lieber aus einer Automatisierung heraus schickt, abonniert contact.created per Webhook und löst sie dort aus.
  • Es ist nicht Ihre Warteschlange. Wenn das CRM kurz nicht erreichbar ist, darf die Anmeldung nicht verloren gehen: Schreiben Sie sie zuerst bei sich weg, senden Sie sie dann, und wiederholen Sie den Versuch bei einem Fehler. Ein Formular, das eine Anmeldung nur an eine fremde API weiterreicht, verliert sie bei jedem Netzwerkfehler.
  • Es dokumentiert die Einwilligung nicht. Wann jemand zugestimmt hat und welchem Text — das ist eine Angabe, die Sie selbst festhalten müssen, entweder in Ihrem System oder in einer eigenen Spalte am Contact. Verwenden Sie dafür keine Spalte, die in Ihrem Workspace schon etwas anderes bedeutet.

Weiter

  • API-Keys: anlegen, rotieren, widerrufen.
  • OpenAPI: die vollständige REST-Referenz mit allen Feldern.
  • Webhooks: die Gegenrichtung — CRM-Events an Ihre Systeme.
Die Richtung stimmt anders, als man erwartet
Schritt 1: ein eigener Benutzer für das Formular
Schritt 2: den Contact anlegen
Schritt 3: dieselbe Adresse zweimal
Was das CRM dabei nicht tut
Weiter