PROPERTY_DOESNT_EXIST : ce code d’erreur renvoyé par l’API HubSpot revient régulièrement dans les journaux d’une extension de synchronisation de formulaires, souvent plusieurs semaines après une mise en production sans accroc apparent. La cause est presque toujours la même : un champ WPForms mappé sur le libellé affiché d’une propriété HubSpot, plutôt que sur son nom interne, qui a changé de son côté sans prévenir personne côté WordPress.
WPForms propose nativement une extension HubSpot dans sa version Pro, mais dès que le formulaire dépasse une poignée de champs simples — choix multiples, propriétés personnalisées, listes déroulantes dépendantes — une intégration sur mesure via l’API REST de HubSpot devient plus fiable que le mapping graphique fourni par défaut. Voici comment la construire sans reproduire les pièges classiques.
Mapper par nom interne, pas par libellé
Chaque propriété HubSpot possède un nom interne stable (internal_name), distinct de son libellé affiché dans l’interface, qui peut lui être renommé à tout moment par un utilisateur métier sans notification technique. Le mapping entre les champs WPForms et les propriétés HubSpot doit toujours reposer sur ce nom interne, récupéré une fois via l’endpoint /crm/v3/properties/contacts et stocké dans les réglages de l’extension :
$response = wp_remote_get(
'https://api.hubapi.com/crm/v3/properties/contacts',
array(
'headers' => array(
'Authorization' => 'Bearer ' . $token,
),
'timeout' => 15,
)
);
Cette liste de propriétés sert à construire un écran de réglages où l’administrateur associe chaque champ WPForms à un nom interne HubSpot, jamais à un libellé traduit en français qui n’a aucune existence côté API.
Ce que la soumission déclenche réellement

Le point d’accroche standard est le hook wpforms_process_complete, déclenché après validation d’une soumission mais avant l’affichage du message de confirmation. C’est là que l’extension construit le tableau de propriétés à envoyer, en utilisant le mapping stocké plutôt qu’une correspondance codée en dur pour un formulaire précis :
add_action( 'wpforms_process_complete', function( $fields, $entry, $form_data ) {
$mapping = get_option( 'crm_sync_field_mapping', array() );
$properties = array();
foreach ( $fields as $field ) {
$wpforms_id = $field['id'];
if ( isset( $mapping[ $wpforms_id ] ) ) {
$properties[ $mapping[ $wpforms_id ] ] = $field['value'];
}
}
crm_sync_queue_contact( $entry['id'], $properties );
}, 10, 3 );
Absorber les erreurs 429 et 5xx sans perdre la soumission
Envoyer directement l’appel à HubSpot depuis le hook de soumission est risqué : un dépassement de quota (erreur 429) ou une indisponibilité temporaire de l’API renvoie une erreur au moment précis où le visiteur attend une confirmation. La bonne pratique consiste à ne jamais appeler l’API en synchrone depuis la requête HTTP du visiteur, mais à empiler la soumission dans une file d’attente traitée par Action Scheduler :
- La fonction
crm_sync_queue_contact()écrit la soumission dans une table dédiée avec un statutpending. - Une tâche planifiée via
as_schedule_single_action()traite la file toutes les minutes. - Sur une erreur
429ou5xx, le statut repasse àpendingavec un compteur de tentatives incrémenté, jusqu’à trois essais. - Au-delà de trois échecs, le statut passe à
failedet une notification apparaît dans l’écran d’administration de l’extension.
Rejouer une synchronisation échouée sans ressaisir le formulaire
Chaque entrée de la file conserve le tableau de propriétés déjà construit, ce qui permet de rejouer manuellement une synchronisation échouée d’un clic depuis l’écran d’administration, sans redemander au visiteur de soumettre à nouveau son formulaire. C’est un détail qui change beaucoup l’expérience du service commercial côté client : un lead capturé n’est jamais perdu, même en cas de panne HubSpot de plusieurs heures.
Conseil qui a évité plusieurs incidents : ne jamais logger le jeton d’API HubSpot dans les journaux d’erreur, même en cas d’échec. Un message d’erreur générique suffit à diagnostiquer, et un jeton qui traîne dans un fichier de log est un risque de sécurité inutile.
Pour aller plus loin
Cette architecture — mapping par nom interne, file d’attente locale, reprise automatique — s’applique de la même façon à n’importe quel autre CRM disposant d’une API REST avec limitation de débit. La leçon générale dépasse HubSpot et WPForms : dès qu’une intégration tierce implique un appel réseau synchrone dans le parcours d’un visiteur, la découpler en file d’attente asynchrone protège autant l’expérience utilisateur que l’intégrité des données transmises.