Skip to content

Tutto per WordPress, lo sviluppo web — e non solo

📤 Invio di moduli in wordpress headless: API REST per Contact Form 7 e Gravity Forms

📤 Invio di moduli in wordpress headless: API REST per Contact Form 7 e Gravity Forms

Stai costruendo un sito su WordPress e hai già sistemato il modulo di contatto. Plugin come Contact Form 7 ti forniscono HTML già pronto, validazione, salvataggio degli invii e decine di integrazioni. Fai clic su «Installa», incolli lo shortcode e in due minuti hai finito.

Ma tutto cambia quando WordPress diventa un CMS headless. Sei responsabile dell'intero frontend: React, Vue, HTML/JS puro. E il plugin per i moduli che prima generava il markup per te non controlla più il lato client. La sua API REST, tuttavia, è ancora lì. Basta inviare una POST all'endpoint corretto e tutta la potenza del plugin (validazione dei campi, salvataggio, integrazioni) resta a tua disposizione.

Nella pratica, puoi coprire anche casi puramente «tradizionali» tramite l'API REST dei plugin per moduli. Mettiamo che tu stia sviluppando un tema personalizzato con Tailwind e il markup fisso di CF7 con la sua rigida struttura di classi stoni. Inviare i dati via API ti permette di controllare ogni pixel del modulo senza abbandonare l'ecosistema consolidato del plugin.

💡 Panoramica rapida:

  • Quali endpoint mettono a disposizione Contact Form 7 e Gravity Forms e come attivarli in un ambiente headless.
  • Quale formato usare per inviare i campi affinché il plugin accetti correttamente i dati e restituisca un risultato.
  • Come costruire un modulo HTML, collegare una richiesta fetch e mostrare all'utente un messaggio di successo o gli errori di validazione.
  • Come unificare i diversi formati di risposta di CF7 e Gravity Forms in un'unica comoda struttura.

Cosa devi sapere sugli endpoint

Inviare dati tramite l'API REST è la parte tecnicamente semplice. Entrambi i plugin si aspettano una POST a un endpoint in cui il segmento dinamico dell'URL è l'identificativo del modulo specifico.

Contact Form 7 mette a disposizione un'API REST subito dopo l'attivazione. L'endpoint ha questo aspetto:

1https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback

A partire dalla versione 5.8 (agosto 2023), Contact Form 7 è passato a identificativi di modulo con hash SHA-1. I vecchi ID numerici funzionano ancora, ma per i nuovi moduli devi recuperare l'identificativo dall'URL della pagina di modifica del modulo nel pannello di amministrazione (l'ultimo segmento dopo post=). Ad aprile 2026, il plugin ha oltre 10 milioni di installazioni attive ed è testato fino a WordPress 7.0.

Gravity Forms utilizza l'API REST v2 (disponibile dalla versione 2.4):

1https://your-site.tld/wp-json/gf/v2/forms/<FORM_ID>/submissions

Nota importante: l'API REST di Gravity Forms è disabilitata per impostazione predefinita. Per attivarla, vai nelle impostazioni del plugin → scheda REST API → seleziona «Abilita l'accesso all'API». Per l'endpoint di invio del modulo non è richiesta una chiave API: è pubblico per progettazione. L'identificativo del modulo in Gravity Forms è numerico e visibile nel pannello di amministrazione durante la modifica.

Struttura del corpo della richiesta

Prendiamo un modulo di esempio con cinque campi: testo obbligatorio, email e data (antecedente al 4 ottobre 1957), un'area di testo facoltativa e una casella di controllo obbligatoria.

Esempio di modulo di contatto con cinque campi di input

Contact Form 7 si aspetta chiavi nel formato definito tramite la sintassi dei form tag. La chiave corrisponde all'attributo name del campo 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 adotta un approccio diverso: identificatori incrementali generati automaticamente con il prefisso input_. L'ID del campo è visibile direttamente nel pannello di amministrazione quando si modifica un campo specifico.

Modifica di un campo Gravity Forms con l'identificatore visibile input_3

Per lo stesso modulo, il corpo della richiesta per Gravity Forms ha un aspetto diverso:

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}

Concetto chiave: se assegni agli input HTML attributi name che corrispondono alle chiavi attese dal plugin, la mappatura avviene in automatico e FormData raccoglie i dati nel formato corretto senza bisogno di mappature manuali.

Costruire l'HTML e inviare la richiesta

Per Contact Form 7, il markup HTML si presenta così (nota che action è l'endpoint e gli attributi name dei campi corrispondono alle chiavi viste sopra):

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>

Per Gravity Forms cambiano solo action e gli attributi name:

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>

Ora l'invio tramite JavaScript: FormData raccoglie i valori tramite name in automatico, quindi non serve alcuna mappatura:

1const 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
25const formElement = document.querySelector("form");
26formElement.addEventListener("submit", formSubmissionHandler);

I dati vengono inviati. Ma questo non basta per l'utente, che ha bisogno di un feedback: un messaggio di successo, l'evidenziazione dei campi con errori, una notifica globale. Per fortuna entrambi i plugin restituiscono queste informazioni nella risposta.

Validazione: il server decide, il client mostra

Oltre alla validazione HTML5 nativa (attributi come required, type="email" e max), conviene affidarsi al controllo delle regole lato server offerto dai plugin. Il motivo: le regole sono configurate centralmente nell'amministrazione di WordPress e duplicarle sul client significa doppio lavoro e una fonte di incongruenze.

Sia Contact Form 7 che Gravity Forms restituiscono gli errori di validazione direttamente nel corpo della risposta. Per scenari complessi (campi condizionali, validazione dipendente), basarsi sulla validazione lato server è particolarmente vantaggioso: non devi sincronizzare la logica tra frontend e impostazioni del plugin.

Il compito si riduce a tre passaggi: fare il parsing della risposta JSON, estrarre i messaggi di errore e inserirli nel DOM accanto ai campi corrispondenti.

Formati di risposta e normalizzazione

Risposta di Contact Form 7 in caso di errore di validazione:

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}

In caso di successo, la risposta è più compatta:

1{
2 "into": "#",
3 "status": "mail_sent",
4 "message": "Thank you for your message. It has been sent.",
5 "posted_data_hash": "d52f9f9de995287195409fe6dcde0c50"
6}

La risposta di Gravity Forms in caso di errore di validazione ha una struttura diversa:

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}

E una risposta di successo contiene la conferma all'interno di 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}

La differenza di approccio è evidente: CF7 incorpora gli errori in un array di oggetti con selettori CSS, mentre Gravity Forms usa un oggetto piatto con chiavi numeriche senza il prefisso input_. Il messaggio di successo di Gravity Forms arriva racchiuso in HTML. Le chiavi dei campi nelle risposte di CF7 sono incorporate nei selettori (ad esempio, span.wpcf7-form-control-wrap.somebodys-name) e richiedono estrazione tramite regex.

Invece di creare una logica ramificata per ciascun plugin, è più comodo normalizzare entrambe le risposte in un formato unificato:

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}

In caso di successo, isSuccess viene impostato su true e validationError è un oggetto vuoto.

Codice di normalizzazione per Contact Form 7:

1const 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};

Codice di normalizzazione per Gravity Forms (nota: alle chiavi di errore viene aggiunto il prefisso input_ in modo che corrispondano alle chiavi della richiesta):

1const 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};

Ora hai un oggetto di risposta unificato, indipendentemente dal plugin. Non resta che scrivere la visualizzazione degli errori e l'attivazione/disattivazione delle classi sugli elementi del DOM, e il form è pronto all'uso.

Dalla normalizzazione a un'interfaccia reattiva

Una volta convertita la risposta in una struttura unificata, mostrare il feedback si riduce a manipolazione del DOM. Aggiungere un messaggio di errore accanto a un campo, attivare una classe su un wrapper e mostrare una notifica globale: queste tre azioni sono sufficienti per la stragrande maggioranza degli scenari.

Per aggiornamenti reattivi dell'interfaccia, librerie dichiarative leggere come Alpine.js sono molto comode. Sintassi minimale, nessun passaggio di build e integrazione naturale con le risposte del server la rendono una scelta pratica per i form in un ambiente headless. L'approccio con Alpine.js è stato trattato in dettaglio su CSS-Tricks; il codice di quel materiale funziona pressoché invariato con la risposta normalizzata che abbiamo ottenuto sopra.

In conclusione

Replicare la funzionalità lato client che i plugin per form offrono «out of the box» richiede un paio d'ore di lavoro per form semplici. Un bel vantaggio: astraendo la risposta tramite una funzione normalizer, si ottiene un backend intercambiabile. Passare da Contact Form 7 a Gravity Forms (o viceversa) si può fare senza modifiche al frontend; basta sostituire l'endpoint e la funzione normalizer.

Form multi-pagina, anteprime di immagini caricate, calcolatori di prezzo: sì, quello è sviluppo impegnativo. Ma più i requisiti di un progetto sono unici, più è forte la motivazione per un frontend personalizzato sopra la REST API: non si lotta contro il markup di qualcun altro né si aggirano i limiti di un rendering predefinito.

L'approccio headless ai form non è un futuro ipotetico. Oggi plugin come Contact Form 7 e Gravity Forms offrono REST API complete, e i framework frontend permettono di costruire un form in ore anziché in giorni. Provatelo sul prossimo progetto in cui l'aspetto del form è critico: usate CF7 o GF come backend e costruite l'interfaccia da zero. Probabilmente resterete sorpresi da quanto sia lineare.

⁉️🤔 Domande frequenti

La REST API di Contact Form 7 funziona con la versione gratuita?

Sì, la REST API è disponibile subito dopo aver attivato il plugin gratuito; non serve alcuna configurazione aggiuntiva. Ad aprile 2026, Contact Form 7 ha oltre 10 milioni di installazioni attive e la REST API è una parte stabile del core del plugin dalla versione 4.8.

Cosa cambia con gli ID form hash nelle versioni più recenti di Contact Form 7?

A partire dalla versione 5.8 (agosto 2023), CF7 genera un hash SHA-1 come identificatore del form invece di un ID numerico. I vecchi ID numerici funzionano ancora. L'hash si trova nell'URL della pagina di modifica del form nel pannello di amministrazione: /wp-admin/admin.php?page=wpcf7&post=<HASH>&action=edit. Si inserisce nell'endpoint allo stesso modo di un ID numerico.

Serve una chiave API per inviare form tramite la REST API di Gravity Forms?

No, l'endpoint /gf/v2/forms/<ID>/submissions non richiede autenticazione per l'invio. Tuttavia, la REST API di Gravity Forms è disabilitata di default; bisogna attivarla nelle impostazioni del plugin (Form → Impostazioni → REST API → Abilita accesso all'API).

Si può usare lo stesso codice JavaScript sia per Contact Form 7 che per Gravity Forms?

Sì, è esattamente lo scopo della normalizzazione della risposta. Entrambe le funzioni normalizer (per CF7 e GF) restituiscono un oggetto con la stessa struttura: campi isSuccess, message e validationError. Si collega la funzione appropriata in base al plugin e tutto il resto del codice (visualizzazione errori, evidenziazione campi, notifica globale) funziona senza modifiche.

Cosa fare se il form non si invia e il server restituisce un 404?

Verificate tre cose: se la REST API del plugin è abilitata (particolarmente rilevante per Gravity Forms), se l'identificativo del form nell'URL dell'endpoint è corretto e se la REST API viene bloccata a livello di server o da un plugin di sicurezza. Per Contact Form 7, assicuratevi anche che la REST API di WordPress sia globalmente attiva; senza di essa, CF7 non può elaborare gli invii AJAX.