Einleitung
Wenn Sie eine Nonprofit- oder Community-Organisation betreiben, möchten Sie wahrscheinlich eine Newsletter-Anmeldung auf Ihrer Website haben. Und wenn Sie CiviCRM als CRM nutzen, sollen neue Abonnent:innen direkt in CiviCRM landen - mit sauberem Double-Opt-In und ohne zusätzlichen Newsletter-Dienst.
CiviCRM Standalone (die neue Deployment-Option, die ohne Drupal, WordPress oder Joomla als Host-CMS läuft) macht das gleichzeitig einfacher und schwieriger. Einfacher, weil das CRM jetzt ein sauber getrennter, eigenständiger Dienst ist. Schwieriger, weil die meisten vorhandenen Anleitungen davon ausgehen, dass CiviCRM in ein CMS eingebettet ist, und viele alte URL-Pfade schlicht nicht funktionieren.
Diese Anleitung zeigt, wie Sie ein Newsletter-Anmeldeformular auf jeder externen Website bauen - egal ob WordPress, statische Hugo-Seite, einfache HTML-Seite oder etwas anderes -, das über die REST API mit CiviCRM Standalone spricht. Das Formular sammelt eine E-Mail-Adresse ein, CiviCRM erstellt oder findet den Kontakt, trägt ihn in Ihre Mailing-Gruppe ein und verschickt automatisch eine Double-Opt-In-Bestätigungsmail. Ich habe mir das meiste davon auf die harte Tour zusammengesucht; hoffentlich spart Ihnen das etwas Zeit.
Voraussetzungen
- Eine laufende CiviCRM Standalone-Installation (z. B. unter
crm.example.com) - Die FormProcessor-Extension, installiert und aktiviert in CiviCRM (über die Extensions-Ansicht oder mit
cv ext:install formprocessor) - Einen API-Key für einen CiviCRM-Benutzer (unter Kontakte -> API-Benutzer suchen -> Reiter API Key)
- Den Site-Key (in
civicrm.settings.php, KonstanteCIVICRM_SITE_KEY)
CiviCRM einrichten
1. Mailing-Gruppe erstellen
Gehen Sie zu Kontakte -> Gruppen verwalten -> Neue Gruppe. Erstellen Sie eine Gruppe (z. B. “Newsletter”) und setzen Sie den Gruppentyp auf “Mailing List”.
Entscheidend: Setzen Sie die Sichtbarkeit auf “Public Pages”.
Das übersieht man leicht.
Die API-Action MailingEventSubscribe, die den Double-Opt-In-Ablauf auslöst, prüft, ob die Zielgruppe öffentlich ist.
Wenn die Sichtbarkeit auf “User and User Admin Only” steht (der Standard), lehnt die API die Anmeldung mit einem “Group is not Public”-Fehler ab.
Kein hilfreicher Hinweis, nur eine klare Absage.
Also: auf “Public Pages” setzen.
2. FormProcessor konfigurieren
FormProcessor ist eine CiviCRM-Extension, mit der Sie wiederverwendbare API-Actions definieren können - im Grunde benannte Prozeduren, die mehrere Schritte bündeln und als eine einzige API-Aktion verfügbar machen.
Wir erstellen eine namens newsletter_signup.
- Gehen Sie zu Administer -> Automation -> FormProcessors
- Klicken Sie auf “Add Form Processor”
- Nennen Sie ihn
newsletter_signup(der technische Name ist wichtig - er wird zum API-Action-Namen) - Fügen Sie einen Eingabeparameter hinzu: Name
email, Typ “String” (oder “Email”, falls verfügbar), als erforderlich markieren - Fügen Sie eine Action zum Finden oder Erstellen eines Kontakts hinzu:
- Action-Typ: “Get or Create Contact by Email”
- Den E-Mail-Eingabeparameter auf diese Action mappen
- Fügen Sie eine zweite Action für die Anmeldung hinzu:
- Action-Typ: “MailingEventSubscribe” (oder eine generische “API Call”-Action mit Entity
MailingEventSubscribeund Actioncreate) - Den Parameter
emailauf den E-Mail-Eingabeparameter setzen group_idauf die ID Ihrer Newsletter-Gruppe setzen (die Gruppen-ID finden Sie in der URL beim Bearbeiten der Gruppe oder im API Explorer)contact_idaus dem Ergebnis der vorherigen “Get or Create Contact”-Action mappen
- Action-Typ: “MailingEventSubscribe” (oder eine generische “API Call”-Action mit Entity
Nach dem Speichern können Sie das im CiviCRM API Explorer testen (Support -> Developer -> API Explorer v4), indem Sie FormProcessor.newsletter_signup mit einer Test-E-Mail-Adresse aufrufen.
Wenn alles funktioniert, verschickt CiviCRM eine Bestätigungsmail mit Double-Opt-In-Link an diese Adresse.
3. API-Key und Site-Key finden
API-Key: Öffnen Sie den Kontakt des Benutzers, den Sie für den API-Zugriff verwenden möchten. Im Reiter API Key können Sie den Schlüssel generieren oder kopieren. Dieser Benutzer braucht ausreichende Berechtigungen, um Kontakte zu erstellen und Gruppenmitgliedschaften zu verwalten.
Site-Key: Öffnen Sie auf dem Server die Datei civicrm.settings.php Ihrer CiviCRM-Standalone-Installation und suchen Sie nach:
define('CIVICRM_SITE_KEY', 'your-site-key-here');
Das Formular bauen
Hier ist ein vollständiges, eigenständiges HTML/CSS/JS-Snippet, das Sie in jede Website einfügen können.
Es spricht die CiviCRM REST API direkt aus dem Browser mit einem einfachen XMLHttpRequest an - keine Abhängigkeiten, kein Build-Schritt, kein jQuery.
Ersetzen Sie die drei Platzhalter-Variablen am Anfang: CIVICRM_URL, API_KEY und SITE_KEY.
<div id="newsletter-signup">
<style>
#newsletter-signup {
max-width: 440px;
margin: 2rem auto;
padding: 2rem;
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 16px rgba(0,0,0,0.10);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
#newsletter-signup h3 {
margin: 0 0 0.5rem 0;
font-size: 1.25rem;
}
#newsletter-signup p {
margin: 0 0 1.2rem 0;
color: #555;
font-size: 0.95rem;
}
#newsletter-signup label {
display: block;
font-weight: 600;
margin-bottom: 0.4rem;
font-size: 0.95rem;
}
#newsletter-signup input[type="email"] {
width: 100%;
padding: 0.6rem 0.8rem;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 1rem;
box-sizing: border-box;
margin-bottom: 1rem;
}
#newsletter-signup input[type="email"]:focus {
outline: none;
border-color: #c0392b;
box-shadow: 0 0 0 2px rgba(192,57,43,0.15);
}
#newsletter-signup button {
width: 100%;
padding: 0.7rem;
background: #c0392b;
color: #fff;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
#newsletter-signup button:hover {
background: #a93226;
}
#newsletter-signup button:disabled {
background: #ccc;
cursor: not-allowed;
}
#newsletter-signup .nl-msg {
margin-top: 1rem;
padding: 0.8rem;
border-radius: 6px;
font-size: 0.95rem;
display: none;
}
#newsletter-signup .nl-msg.success {
background: #eafaf1;
color: #1e7e34;
border: 1px solid #b7e4c7;
display: block;
}
#newsletter-signup .nl-msg.error {
background: #fdecea;
color: #a94442;
border: 1px solid #f5c6cb;
display: block;
}
</style>
<h3>Newsletter abonnieren</h3>
<p>Erhalten Sie Updates direkt in Ihr Postfach. Wir respektieren Ihre Privatsphäre.</p>
<form id="nl-form" onsubmit="return nlSubmit(event)">
<label for="nl-email">E-Mail-Adresse</label>
<input type="email" id="nl-email"
placeholder="name@beispiel.de" required>
<button type="submit" id="nl-btn">Abonnieren</button>
</form>
<div id="nl-msg" class="nl-msg"></div>
<script>
function nlSubmit(e) {
e.preventDefault();
// ===== DIESE DREI WERTE ERSETZEN =====
var CIVICRM_URL = "https://crm.example.com";
var API_KEY = "YOUR_API_KEY";
var SITE_KEY = "YOUR_SITE_KEY";
// ======================================
var email = document.getElementById("nl-email").value.trim();
var btn = document.getElementById("nl-btn");
var msg = document.getElementById("nl-msg");
if (!email) return false;
btn.disabled = true;
btn.textContent = "Wird gesendet...";
msg.className = "nl-msg";
msg.style.display = "none";
var url = CIVICRM_URL
+ "/civicrm/ajax/rest"
+ "?entity=FormProcessor"
+ "&action=newsletter_signup"
+ "&json=1"
+ "&api_key=" + encodeURIComponent(API_KEY)
+ "&key=" + encodeURIComponent(SITE_KEY)
+ "&email=" + encodeURIComponent(email);
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
btn.disabled = false;
btn.textContent = "Abonnieren";
if (xhr.status === 200) {
try {
var resp = JSON.parse(xhr.responseText);
if (resp.is_error && resp.is_error !== 0) {
msg.textContent = "Fehler: " + (resp.error_message || "Unbekannter Fehler");
msg.className = "nl-msg error";
} else {
msg.textContent = "Vielen Dank! Bitte prüfen Sie Ihr Postfach "
+ "und klicken Sie auf den Bestätigungslink, um "
+ "Ihre Anmeldung abzuschließen (Double Opt-In).";
msg.className = "nl-msg success";
document.getElementById("nl-email").value = "";
}
} catch(ex) {
msg.textContent = "Unerwartete Antwort vom Server.";
msg.className = "nl-msg error";
}
} else {
msg.textContent = "Anfrage fehlgeschlagen (HTTP " + xhr.status
+ "). Bitte versuchen Sie es später erneut.";
msg.className = "nl-msg error";
}
};
xhr.send();
return false;
}
</script>
</div>
Dieses Snippet ist vollständig eigenständig.
Die Styles sind auf den Wrapper #newsletter-signup beschränkt, damit sie nicht mit den vorhandenen Styles Ihrer Website kollidieren.
Es rendert eine schlichte Karte mit Schatten, abgerundeten Ecken und rotem Absende-Button.
CORS konfigurieren
Wenn Ihre Website (z. B. www.example.com) und CiviCRM (z. B. crm.example.com) auf unterschiedlichen Domains liegen, blockiert der Browser den AJAX-Request, sofern der CiviCRM-Server nicht die passenden CORS-Header sendet.
Für Apache fügen Sie Folgendes in die .htaccess im Webroot Ihrer CiviCRM-Standalone-Installation ein:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "https://www.example.com"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
</IfModule>
Wenn Sie mehrere Origins erlauben müssen, brauchen Sie eine dynamischere Konfiguration mit SetEnvIf oder einer Rewrite-Regel.
Für eine schnelle Entwicklungsumgebung können Sie * als Origin verwenden; in Produktion sollten Sie immer auf Ihre tatsächliche Domain einschränken.
Für nginx verwenden Sie add_header-Direktiven im Server- oder Location-Block:
location / {
add_header Access-Control-Allow-Origin "https://www.example.com" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
# Preflight-Requests beantworten
if ($request_method = OPTIONS) {
return 204;
}
}
Starten Sie nach dem Hinzufügen der CORS-Header den Webserver neu und testen Sie erneut.
Öffnen Sie die Entwicklerkonsole des Browsers und prüfen Sie, dass der Request an crm.example.com keinen CORS-Fehler mehr zeigt.
WordPress-Integration
Wenn Ihre öffentliche Website auf WordPress läuft, haben Sie je nach Theme zwei Optionen: klassisches Theme oder Block-Theme.
Option A: Contact Form 7 + CiviCRM Integration Plugin (klassische Themes)
Dieser Ansatz funktioniert gut mit klassischen Themes und gibt Ihnen ein Formular, das Sie im WordPress-Admin verwalten können.
- Installieren und aktivieren Sie das Plugin Contact Form 7
- Installieren und aktivieren Sie das Plugin “Contact Form 7 CiviCRM Integration”
- Erstellen Sie ein neues Contact-Form-7-Formular mit einem E-Mail-Feld
- Konfigurieren Sie im CiviCRM-Integration-Tab des Formulars:
- CiviCRM Host:
https://crm.example.com - API Key: Ihr API-Key
- Site Key: Ihr Site-Key
- REST Path:
/civicrm/ajax/rest
- CiviCRM Host:
- Setzen Sie die Action auf
FormProcessor.newsletter_signupund mappen Sie das E-Mail-Feld
Wichtig: Achten Sie darauf, dass der REST-Pfad auf /civicrm/ajax/rest gesetzt ist und nicht auf den alten Pfad /sites/all/modules/civicrm/extern/rest.php oder /core/extern/rest.php.
CiviCRM Standalone hat keine civicrm.config.php an der Stelle, an der extern/rest.php sie erwartet; dieser Endpunkt liefert deshalb einen HTTP-500-Fehler.
Die Route /civicrm/ajax/rest läuft durch das normale CiviCRM-Routing und funktioniert korrekt mit Standalone.
Option B: HTML-Block im Block-Editor (Block-Themes)
Wenn Sie ein modernes Block-Theme verwenden (oder einfach keine zusätzlichen Plugins installieren möchten), können Sie das HTML/JS-Snippet direkt in einen Custom HTML-Block im WordPress-Block-Editor einfügen.
Das funktioniert - mit einer wichtigen Einschränkung.
WordPress entfernt <script>, <form> und <input>-Tags.
WordPress hat einen Sicherheitsfilter namens kses, der HTML-Inhalte bereinigt.
Wenn Sie eine Seite über den Block-Editor speichern (der intern die REST API nutzt), läuft kses und entfernt still alle Tags, die als unsicher gelten - darunter <script>, <form> und oft auch <input>.
Sie fügen das Snippet ein, klicken auf Speichern, laden die Seite neu und stellen fest: Formular und JavaScript sind einfach verschwunden.
Die Lösung ist, kses-Filtering für REST-API-Requests zu deaktivieren.
Legen Sie dafür ein kleines Must-Use-Plugin (mu-plugin) unter wp-content/mu-plugins/disable-kses-rest.php an:
<?php
/**
* Plugin Name: Disable kses for REST API
* Description: Verhindert, dass WordPress script/form/input-Tags
* beim Speichern via REST API entfernt (Block-Editor).
*/
add_action('rest_api_init', function() {
if (current_user_can('unfiltered_html')) {
kses_remove_filters();
}
});
Das prüft, ob der aktuelle Benutzer die Berechtigung unfiltered_html hat (Administratoren haben sie standardmäßig), bevor der Filter deaktiviert wird.
So können nur vertrauenswürdige Benutzer ungefiltertes HTML speichern, während die Einschränkung für niedrigere Rollen bestehen bleibt.
Gehen Sie nach dem Anlegen der Datei zurück zur Seite, fügen Sie das HTML/JS-Snippet in einen Custom-HTML-Block ein und speichern Sie. Diesmal bleiben Formular- und Script-Tags erhalten.
Integration in andere CMS / statische Websites
Das HTML/JS-Snippet von oben ist einfacher, dependency-freier Code. Es funktioniert überall, wo Sie HTML und JavaScript platzieren können. Ein paar konkrete Hinweise:
- Hugo, Jekyll, 11ty oder andere Static Site Generators:
Erstellen Sie ein Partial oder Include (z. B.
newsletter-form.html) mit dem Snippet und binden Sie es in Ihre Seitentemplates ein. In Hugo:{{ partial "newsletter-form.html" . }}. In Jekyll:{% include newsletter-form.html %}. In 11ty: Verwenden Sie ein Nunjucks-Include oder einen Shortcode. - Drupal:
Nutzen Sie einen “Custom Block” mit Full-HTML-Format oder binden Sie das Snippet direkt in ein Twig-Template ein.
Achten Sie darauf, dass Ihr Textformat
<script>-Tags erlaubt (das Format “Full HTML” tut das normalerweise). - Joomla: Verwenden Sie ein Custom-HTML-Modul oder ein “Custom Code”-Element im Artikel. Stellen Sie auch hier sicher, dass der Editor Script-Tags nicht entfernt (in die Source-/Code-Ansicht wechseln).
- Einfache HTML-Seite: Einfach einfügen. Keine besonderen Hinweise.
Sie brauchen nur eine Seite, die über HTTPS ausgeliefert wird (für sichere API-Aufrufe), und korrekt konfigurierte CORS-Header auf dem CiviCRM-Server, falls die Domains unterschiedlich sind.
Troubleshooting / Häufige Fallstricke
Hier sind die Probleme, über die ich gestolpert bin, damit Sie direkt zur Lösung springen können.
extern/rest.php gibt HTTP 500 zurück
Wenn Sie https://crm.example.com/core/extern/rest.php (oder irgendeinen extern/rest.php-Pfad) aufrufen und einen 500er bekommen, liegt das daran, dass CiviCRM Standalone die Datei civicrm.config.php nicht an der Stelle ablegt, an der extern/rest.php sie laden möchte.
Die Lösung ist einfach: Verwenden Sie stattdessen /civicrm/ajax/rest als API-Endpunkt.
Das ist die moderne, unterstützte Route für die REST API von CiviCRM Standalone.
“Group is not Public”-Fehler
Wenn Sie beim Aufruf von MailingEventSubscribe.create diesen Fehler sehen, ist die Sichtbarkeit Ihrer Ziel-Mailing-Gruppe auf “User and User Admin Only” gesetzt.
Gehen Sie zu Kontakte -> Gruppen verwalten, bearbeiten Sie die Gruppe und ändern Sie die Sichtbarkeit auf “Public Pages”.
Die MailingEventSubscribe-API erzwingt diese Prüfung, weil der Double-Opt-In-Ablauf für öffentliche Anmeldungen gedacht ist.
CORS-Fehler in der Browser-Konsole
Wenn Sie Fehler wie Access to XMLHttpRequest at 'https://crm.example.com/...' from origin 'https://www.example.com' has been blocked by CORS policy sehen, sendet Ihr CiviCRM-Server nicht die richtigen CORS-Header.
Siehe Abschnitt CORS-Konfiguration oben.
Denken Sie daran, Apache oder nginx nach dem Hinzufügen der Header neu zu starten.
Beachten Sie außerdem, dass Browser CORS-Preflight-Antworten recht aggressiv cachen können - machen Sie einen Hard Refresh oder testen Sie in einem privaten Fenster.
WordPress entfernt Formular-/Script-Tags
Sie fügen das HTML-Snippet in einen Custom-HTML-Block ein, speichern, und die <script>- und <form>-Tags verschwinden.
Das ist die kses-Sanitization von WordPress.
Der Block-Editor speichert Inhalte über die REST API, und kses läuft bei REST-API-Requests.
Installieren Sie das mu-plugin aus dem WordPress-Abschnitt oben, um kses für Admin-Benutzer bei REST-API-Requests zu deaktivieren.
API-Berechtigungsfehler bei System.get
Einige CiviCRM-API-Integrationen (z. B. das CF7-CiviCRM-Plugin) rufen System.get auf, um die Verbindung zu validieren.
Diese API-Action benötigt die Berechtigung “administer CiviCRM”.
Wenn Ihr API-Benutzer diese Berechtigung nicht hat, schlägt der Verbindungstest fehl - auch wenn die eigentliche Kontaktanlage und Gruppenanmeldung problemlos ohne diese Berechtigung funktionieren würden.
Geben Sie dem API-Benutzer entweder die Berechtigung “administer CiviCRM”, oder - falls Ihre Security Policy das verhindert - behalten Sie im Kopf, dass die eigentliche Newsletter-Anmeldung trotzdem funktioniert; nur der Verbindungstest scheitert.
Das war’s. Wenn alles verbunden ist, läuft der Ablauf so: Besucher:in trägt E-Mail ein -> Ihr Formular ruft die REST API von CiviCRM auf -> FormProcessor erstellt/findet den Kontakt und meldet ihn an -> CiviCRM verschickt eine Double-Opt-In-Bestätigungsmail -> Besucher:in klickt den Link -> die Anmeldung ist abgeschlossen. Kein zusätzlicher Newsletter-Dienst, volle Kontrolle über Ihre Daten, und es funktioniert mit jedem Frontend.