
📤 Formulare in headless WordPress übermitteln: REST API für Contact Form 7 und Gravity Forms
Sie erstellen eine Website mit WordPress, und ein Kontaktformular ist bereits eingerichtet. Plugins wie Contact Form 7 liefern Ihnen fertiges HTML, Validierung, Speicherung der Einreichungen und Dutzende Integrationen. Klicken Sie auf „Installieren", fügen Sie den Shortcode ein, und in zwei Minuten ist alles erledigt.
Doch alles ändert sich, wenn WordPress als Headless-CMS betrieben wird. Sie tragen die Verantwortung für das gesamte Frontend: React, Vue, reines HTML/JS. Und das Formular-Plugin, das zuvor das Markup für Sie gerendert hat, kontrolliert nicht mehr die Client-Seite. Seine REST-API ist jedoch weiterhin vorhanden. Senden Sie einfach einen POST an den richtigen Endpunkt, und die gesamte Leistungsfähigkeit des Plugins (Feldvalidierung, Speicherung, Integrationen) steht Ihnen weiterhin zur Verfügung.
In der Praxis können Sie auch rein „traditionelle" Fälle über die REST-API der Formular-Plugins abdecken. Angenommen, Sie entwickeln ein Custom-Theme mit Tailwind, und das starre Markup von CF7 mit seiner unflexiblen Klassenstruktur wirkt fehl am Platz. Die Übermittlung über die API erlaubt es Ihnen, jedes Pixel des Formulars zu kontrollieren, ohne das etablierte Ökosystem des Plugins aufzugeben.
💡 Kurzüberblick:
- Welche Endpunkte Contact Form 7 und Gravity Forms bereitstellen und wie Sie diese in einer Headless-Umgebung aktivieren.
- Welches Format Sie beim Senden der Felder verwenden, damit das Plugin die Daten korrekt annimmt und ein Ergebnis zurückgibt.
- Wie Sie ein HTML-Formular erstellen, einen Fetch-Request anhängen und dem Nutzer eine Erfolgsmeldung oder Validierungsfehler anzeigen.
- Wie Sie die unterschiedlichen Antwortformate von CF7 und Gravity Forms in eine einheitliche, praktische Struktur überführen.
Was Sie über Endpunkte wissen müssen
Das Senden von Daten über die REST-API ist der technisch einfache Teil. Beide Plugins erwarten einen POST an einen Endpunkt, bei dem das dynamische URL-Segment die Kennung des jeweiligen Formulars ist.
Contact Form 7 stellt unmittelbar nach der Aktivierung eine REST-API bereit. Der Endpunkt sieht wie folgt aus:
1 https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback
Ab Version 5.8 (August 2023) verwendet Contact Form 7 SHA-1-gehashte Formular-Kennungen. Alte numerische IDs funktionieren weiterhin, aber für neue Formulare müssen Sie die Kennung aus der URL der Formular-Bearbeitungsseite im Admin-Panel entnehmen (das letzte Segment nach post=). Stand April 2026 hat das Plugin über 10 Millionen aktive Installationen und ist bis WordPress 7.0 getestet.
Gravity Forms verwendet die REST-API v2 (verfügbar seit Version 2.4):
1 https://your-site.tld/wp-json/gf/v2/forms/<FORM_ID>/submissions
Wichtiger Hinweis: Die Gravity Forms REST-API ist standardmäßig deaktiviert. Um sie zu aktivieren, gehen Sie zu den Plugin-Einstellungen → Tab „REST API" → aktivieren Sie „Zugriff auf die API erlauben". Für den Endpunkt zur Formularübermittlung ist kein API-Schlüssel erforderlich; er ist bewusst öffentlich. Die Formular-Kennung bei Gravity Forms ist numerisch und im Admin-Panel beim Bearbeiten sichtbar.
Struktur des Request-Bodys
Nehmen wir ein Beispielformular mit fünf Feldern: Pflichttext, E-Mail und Datum (vor dem 4. Oktober 1957), eine optionale Textarea und eine Pflicht-Checkbox.

Contact Form 7 erwartet Schlüssel in dem Format, das über die Formular-Tag-Syntax definiert wird. Der Schlüssel entspricht dem name des jeweiligen Feldes im HTML:
1 { 2 "somebodys-name": "Marian Kenney", 3 "any-email": "[email protected]", 4 "before-space-age": "1922-03-11", 5 "optional-message": "", 6 "fake-terms": "1" 7 }
Gravity Forms verfolgt einen anderen Ansatz: automatisch generierte, inkrementelle Kennungen mit dem Präfix input_. Die Feld-ID ist direkt im Admin-Bereich sichtbar, wenn Sie ein bestimmtes Feld bearbeiten.

Für dasselbe Formular sieht der Request-Body bei Gravity Forms anders aus:
1 { 2 "input_1": "Marian Kenney", 3 "input_2": "[email protected]", 4 "input_3": "1922-03-11", 5 "input_4": "", 6 "input_5_1": "1" 7 }
Kernaussage: Wenn Sie Ihren HTML-Eingabefeldern name-Attribute geben, die den vom Plugin erwarteten Schlüsseln entsprechen, erfolgt die Zuordnung automatisch und FormData erfasst die Daten im korrekten Format, ohne manuelles Mapping.
HTML aufbauen und die Anfrage senden
Für Contact Form 7 sieht das HTML-Markup so aus (beachten Sie, dass action der Endpunkt ist und die name-Attribute der Felder mit den obigen Schlüsseln übereinstimmen):
1 <form action="https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback" method="post"> 2 <label for="somebodys-name">Your name</label> 3 <input id="somebodys-name" type="text" name="somebodys-name" required> 4 5 <label for="any-email">Email</label> 6 <input id="any-email" type="email" name="any-email" required> 7 8 <label for="before-space-age">Date</label> 9 <input id="before-space-age" type="date" name="before-space-age" max="1957-10-04" required> 10 11 <label for="optional-message">Message</label> 12 <textarea id="optional-message" name="optional-message"></textarea> 13 14 <label> 15 <input type="checkbox" name="fake-terms" value="1" required> 16 I accept the terms 17 </label> 18 19 <button type="submit">Submit</button> 20 </form>
Bei Gravity Forms ändern sich nur action und die name-Attribute:
1 <form action="https://your-site.tld/wp-json/gf/v2/forms/<FORM_ID>/submissions" method="post"> 2 <label for="input_1">Your name</label> 3 <input id="input_1" type="text" name="input_1" required> 4 <!-- ... --> 5 </form>
Nun zur Übermittlung per JavaScript: FormData sammelt die Werte automatisch anhand des name-Attributs ein, sodass kein Mapping erforderlich ist:
1 const formSubmissionHandler = (event) => { 2 event.preventDefault(); 3 4 const formElement = event.target; 5 const { action, method } = formElement; 6 const body = new FormData(formElement); 7 8 fetch(action, { method, body }) 9 .then((response) => response.json()) 10 .then((response) => { 11 if (isFormSubmissionError(response)) { 12 // Handle validation errors 13 handleValidationErrors(response); 14 return; 15 } 16 // Successful submission 17 handleSuccess(response); 18 }) 19 .catch((error) => { 20 // Network error or server unavailable 21 handleNetworkError(error); 22 }); 23 }; 24 25 const formElement = document.querySelector("form"); 26 formElement.addEventListener("submit", formSubmissionHandler);
Die Daten sind gesendet. Für den Nutzer reicht das jedoch nicht; er benötigt eine Rückmeldung: eine Erfolgsmeldung, die Hervorhebung fehlerhafter Felder, einen globalen Hinweis. Glücklicherweise liefern beide Plugins diese Informationen in der Antwort mit.
Validierung: Der Server entscheidet, der Client zeigt an
Über die eingebaute HTML5-Validierung hinaus (Attribute wie required, type="email" und max) ist es sinnvoll, sich auf die serverseitige Regelprüfung zu stützen, die die Plugins bereitstellen. Der Grund: Die Regeln werden zentral in der WordPress-Administration konfiguriert, und eine Duplizierung auf Client-Seite bedeutet doppelte Arbeit und eine Quelle für Inkonsistenzen.
Sowohl Contact Form 7 als auch Gravity Forms liefern Validierungsfehler direkt im Response-Body zurück. Bei komplexen Szenarien (bedingte Felder, abhängige Validierung) ist der Rückgriff auf die serverseitige Validierung besonders vorteilhaft: Sie müssen keine Logik zwischen Frontend und Plugin-Einstellungen synchronisieren.
Die Aufgabe läuft auf drei Schritte hinaus: die JSON-Antwort parsen, Fehlermeldungen extrahieren und sie neben den entsprechenden Feldern ins DOM einfügen.
Antwortformate und Normalisierung
Contact Form 7-Antwort bei einem Validierungsfehler:
1 { 2 "into": "#", 3 "status": "validation_failed", 4 "message": "One or more fields have an error. Please check and try again.", 5 "posted_data_hash": "", 6 "invalid_fields": [ 7 { 8 "into": "span.wpcf7-form-control-wrap.somebodys-name", 9 "message": "The field is required.", 10 "idref": null, 11 "error_id": "-ve-somebodys-name" 12 } 13 ] 14 }
Bei Erfolg fällt die Antwort kompakter aus:
1 { 2 "into": "#", 3 "status": "mail_sent", 4 "message": "Thank you for your message. It has been sent.", 5 "posted_data_hash": "d52f9f9de995287195409fe6dcde0c50" 6 }
Die Antwort von Gravity Forms bei einem Validierungsfehler ist anders strukturiert:
1 { 2 "is_valid": false, 3 "validation_messages": { 4 "1": "This field is required.", 5 "2": "This field is required.", 6 "3": "This field is required.", 7 "5": "This field is required." 8 }, 9 "page_number": 1, 10 "source_page_number": 1 11 }
Und eine erfolgreiche Antwort enthält die Bestätigung innerhalb von HTML:
1 { 2 "is_valid": true, 3 "page_number": 0, 4 "source_page_number": 1, 5 "confirmation_message": "<div>Thanks for contacting us! We will get in touch with you shortly.</div>", 6 "confirmation_type": "message" 7 }
Der Unterschied in der Herangehensweise ist offensichtlich: CF7 bettet Fehler in ein Array von Objekten mit CSS-Selektoren ein, während Gravity Forms ein flaches Objekt mit numerischen Schlüsseln ohne das Präfix input_ verwendet. Die Erfolgsmeldung von Gravity Forms wird in HTML verpackt geliefert. Feldschlüssel sind in CF7-Antworten in Selektoren eingebettet (z. B. span.wpcf7-form-control-wrap.somebodys-name) und erfordern eine Extraktion per Regex.
Statt für jedes Plugin eine eigene Verzweigungslogik zu schreiben, ist es bequemer, beide Antworten in ein einheitliches Format zu normalisieren:
1 { 2 "isSuccess": false, 3 "message": "One or more fields have an error. Please check and try again.", 4 "validationError": { 5 "somebodys-name": "The field is required.", 6 "any-email": "The field is required.", 7 "input_3": "The field is required.", 8 "input_5": "This field is required." 9 } 10 }
Im Erfolgsfall wird isSuccess auf true gesetzt und validationError ist ein leeres Objekt.
Normalisierungscode für Contact Form 7:
1 const normalizeContactForm7Response = (response) => { 2 const isSuccess = response.status === 'mail_sent'; 3 const message = isSuccess 4 ? response.message 5 : response.message || 'One or more fields have an error.'; 6 7 const validationError = isSuccess 8 ? {} 9 : Object.fromEntries( 10 response.invalid_fields.map((error) => { 11 const key = /cf7[-a-z]*.(.*)/.exec(error.into)[1]; 12 return [key, error.message]; 13 }) 14 ); 15 16 return { isSuccess, message, validationError }; 17 };
Normalisierungscode für Gravity Forms (beachten Sie: Fehlerschlüssel erhalten das Präfix input_, damit sie mit den Request-Schlüsseln übereinstimmen):
1 const normalizeGravityFormsResponse = (response) => { 2 const isSuccess = response.is_valid; 3 const message = isSuccess 4 ? stripHtml(response.confirmation_message) 5 : 'There was a problem with your submission.'; 6 7 const validationError = isSuccess 8 ? {} 9 : Object.fromEntries( 10 Object.entries(response.validation_messages).map(([key, value]) => [ 11 `input_${key}`, 12 value, 13 ]) 14 ); 15 16 return { isSuccess, message, validationError }; 17 };
Nun haben Sie ein einheitliches Antwortobjekt, unabhängig vom Plugin. Es bleibt nur noch, die Fehleranzeige und das Umschalten von Klassen an DOM-Elementen zu implementieren, und das Formular ist einsatzbereit.
Von der Normalisierung zur Live-Oberfläche
Sobald die Antwort in eine einheitliche Struktur überführt ist, läuft die Anzeige von Feedback auf DOM-Manipulation hinaus. Eine Fehlermeldung neben einem Feld einfügen, eine Klasse an einem Wrapper umschalten und eine globale Benachrichtigung anzeigen: Diese drei Aktionen genügen für die allermeisten Szenarien.
Für reaktive Oberflächenaktualisierungen sind schlanke deklarative Bibliotheken wie Alpine.js praktisch. Minimale Syntax, kein Build-Schritt und die natürliche Integration mit Serverantworten machen es zu einer zweckmäßigen Wahl für Formulare in einer Headless-Umgebung. Der Alpine.js-Ansatz wurde ausführlich auf CSS-Tricks behandelt; der Code aus diesem Material funktioniert nahezu unverändert mit der normalisierten Antwort, die wir oben erhalten haben.
Das Fazit
Die clientseitige Funktionalität, die Formular-Plugins „out of the box" bieten, nachzubilden, ist bei einfachen Formularen eine Sache von wenigen Stunden Arbeit. Ein angenehmer Nebeneffekt: Indem Sie die Antwort über eine Normalisierungsfunktion abstrahieren, erhalten Sie ein austauschbares Backend. Der Wechsel von Contact Form 7 zu Gravity Forms (oder umgekehrt) ist ohne Frontend-Änderungen möglich; ersetzen Sie einfach den Endpunkt und die Normalisierungsfunktion.
Mehrseitige Formulare, Vorschauen hochgeladener Bilder, Preisrechner: Ja, das ist ernsthafte Entwicklung. Aber je einzigartiger die Anforderungen eines Projekts sind, desto stärker spricht dies für ein eigenes Frontend auf Basis der REST-API: Sie kämpfen nicht gegen fremdes Markup und umgehen nicht die Einschränkungen vorgefertigter Darstellung.
Der Headless-Ansatz für Formulare ist keine hypothetische Zukunft. Heute bieten Plugins wie Contact Form 7 und Gravity Forms vollwertige REST-APIs, und Frontend-Frameworks ermöglichen es Ihnen, ein Formular in Stunden statt in Tagen zu erstellen. Probieren Sie es bei Ihrem nächsten Projekt aus, bei dem das Erscheinungsbild des Formulars entscheidend ist: Nutzen Sie CF7 oder GF als Backend und bauen Sie die Oberfläche von Grund auf neu. Sie werden wahrscheinlich überrascht sein, wie unkompliziert das ist.
⁉️🤔 Häufig gestellte Fragen
Funktioniert die Contact Form 7 REST-API mit der kostenlosen Version?
Ja, die REST-API ist sofort nach der Aktivierung des kostenlosen Plugins verfügbar; es ist keine zusätzliche Konfiguration erforderlich. Stand April 2026 hat Contact Form 7 über 10 Millionen aktive Installationen, und die REST-API ist seit Version 4.8 ein stabiler Bestandteil des Plugin-Kerns.
Was ist anders bei den gehashten Formular-IDs in neueren Versionen von Contact Form 7?
Ab Version 5.8 (August 2023) generiert CF7 einen SHA-1-Hash als Formularbezeichner anstelle einer numerischen ID. Alte numerische IDs funktionieren weiterhin. Den Hash finden Sie in der URL der Formularbearbeitungsseite im Admin-Panel:
/wp-admin/admin.php?page=wpcf7&post=<HASH>&action=edit. Er wird im Endpunkt auf die gleiche Weise wie eine numerische ID eingesetzt.
Wird ein API-Schlüssel benötigt, um Formulare über die Gravity Forms REST-API zu senden?
Nein, der Endpunkt
/gf/v2/forms/<ID>/submissionserfordert keine Authentifizierung für die Übermittlung. Allerdings ist die Gravity Forms REST-API selbst standardmäßig deaktiviert; Sie müssen sie in den Plugin-Einstellungen aktivieren (Formulare → Einstellungen → REST-API → Zugriff auf die API aktivieren).
Kann derselbe JavaScript-Code sowohl für Contact Form 7 als auch für Gravity Forms verwendet werden?
Ja, genau dafür ist die Antwortnormalisierung gedacht. Beide Normalisierungsfunktionen (für CF7 und GF) geben ein Objekt mit derselben Struktur zurück: die Felder
isSuccess,messageundvalidationError. Binden Sie die entsprechende Funktion je nach Plugin ein, und der gesamte übrige Code (Fehleranzeige, Feldhervorhebung, globale Benachrichtigung) funktioniert ohne Änderungen.
Was soll ich tun, wenn das Formular nicht gesendet wird und der Server einen 404-Fehler zurückgibt?
Prüfen Sie drei Dinge: ob die REST-API des Plugins aktiviert ist (besonders relevant für Gravity Forms), ob der Formularbezeichner in der Endpunkt-URL korrekt ist und ob die REST-API auf Serverebene oder durch ein Sicherheits-Plugin blockiert wird. Stellen Sie bei Contact Form 7 außerdem sicher, dass die WordPress REST-API global aktiv ist; ohne sie kann CF7 keine AJAX-Übermittlungen verarbeiten.



