# Un bloc et HubSpot : synchroniser sans dépendre d’un webhook fragile

> Architecture de synchronisation bidirectionnelle entre un bloc de formulaire et HubSpot, basée sur une file d'attente plutôt que sur des webhooks directs peu fiables.

- Auteur : Clément Hadrot
- Publié le : 2026-03-15
- Mis à jour le : 2026-03-15
- Catégorie : Blocs Gutenberg
- URL : https://wpmoderne.dev.wordpress-developpement.fr/blocs/hubspot-bloc-synchronisation-file-attente/

## L’essentiel

- File d'attente locale absorbant les échecs temporaires de l'API HubSpot
- Traitement différé via wp-cron plutôt qu'un appel synchrone bloquant
- Rejeu automatique des tentatives échouées avec recul exponentiel

Un webhook HubSpot qui échoue silencieusement pendant une maintenance de l'API, un formulaire soumis pendant cette fenêtre, et la donnée du prospect qui disparaît sans que personne ne s'en aperçoive avant plusieurs semaines : ce scénario, vécu sur un projet de génération de leads, a conduit à repenser entièrement l'architecture de synchronisation entre un bloc de formulaire et HubSpot.

Cette architecture ne couvre pas la configuration du CRM HubSpot lui-même (pipelines, propriétés personnalisées déjà en place) ni les workflows marketing qui exploitent ensuite ces données, uniquement le mécanisme de synchronisation entre le bloc et l'API HubSpot.

## Pourquoi un appel direct synchrone est fragile

L'architecture initiale appelait l'API HubSpot directement au moment de la soumission du formulaire, dans le même cycle de requête que la réponse envoyée au visiteur. Toute latence ou indisponibilité côté HubSpot se répercutait directement sur l'expérience du visiteur, qui patientait sur un écran de chargement, et toute erreur silencieuse (timeout réseau, quota d'API dépassé) faisait disparaître la donnée sans trace exploitable.

## La file d'attente locale comme tampon

La nouvelle architecture introduit une table personnalisée, `wp_hubspot_file_synchronisation`, qui enregistre chaque soumission de formulaire avant tout appel à l'API HubSpot. Le visiteur reçoit une confirmation immédiate dès l'écriture en base locale, sans attendre la réponse de HubSpot :

```
function contact_bloc_enregistrer_soumission( array $donnees ) {
    global $wpdb;

    $wpdb->insert(
        $wpdb->prefix . 'hubspot_file_synchronisation',
        array(
            'donnees'       => wp_json_encode( $donnees ),
            'statut'        => 'en_attente',
            'tentatives'    => 0,
            'date_creation' => current_time( 'mysql' ),
        )
    );

    return true; // Confirmation immédiate, indépendante de HubSpot.
}
```

## Le traitement différé via wp-cron

Un événement `wp-cron` planifié toutes les cinq minutes traite les entrées en attente, par lots limités pour ne pas saturer les quotas d'appels de l'API HubSpot :

```
function contact_traiter_file_hubspot() {
    global $wpdb;

    $lignes = $wpdb->get_results(
        "SELECT * FROM {$wpdb->prefix}hubspot_file_synchronisation
         WHERE statut = 'en_attente' AND tentatives < 3
         ORDER BY date_creation ASC
         LIMIT 20"
    );

    foreach ( $lignes as $ligne ) {
        contact_tenter_synchronisation_hubspot( $ligne );
    }
}
add_action( 'contact_synchronisation_hubspot', 'contact_traiter_file_hubspot' );
```

> L'essentiel à retenir : File d'attente locale absorbant les échecs temporaires de l'API HubSpot ; Traitement différé via wp-cron plutôt qu'un appel synchrone bloquant ; Rejeu automatique des tentatives échouées avec recul exponentiel

## Le recul exponentiel entre les tentatives

Chaque échec incrémente le compteur de tentatives et calcule un délai avant la prochaine tentative, croissant à chaque échec plutôt que fixe, pour éviter de marteler une API HubSpot déjà en difficulté :

```
function contact_tenter_synchronisation_hubspot( $ligne ) {
    global $wpdb;

    $reponse = wp_remote_post( HUBSPOT_ENDPOINT_CONTACTS, array(
        'headers' => array( 'Authorization' => 'Bearer ' . HUBSPOT_TOKEN ),
        'body'    => $ligne->donnees,
        'timeout' => 10,
    ) );

    if ( is_wp_error( $reponse ) || 200 !== wp_remote_retrieve_response_code( $reponse ) ) {
        $delai_minutes = pow( 5, $ligne->tentatives + 1 ); // 5, 25, 125 minutes
        $wpdb->update(
            $wpdb->prefix . 'hubspot_file_synchronisation',
            array(
                'tentatives'      => $ligne->tentatives + 1,
                'prochaine_tentative' => date( 'Y-m-d H:i:s', strtotime( "+{$delai_minutes} minutes" ) ),
            ),
            array( 'id' => $ligne->id )
        );
        return;
    }

    $wpdb->update(
        $wpdb->prefix . 'hubspot_file_synchronisation',
        array( 'statut' => 'synchronise' ),
        array( 'id' => $ligne->id )
    );
}
```

## Ce que ça change concrètement

- Le visiteur ne subit plus jamais la latence ou l'indisponibilité de l'API HubSpot.
- Chaque échec reste visible et rejouable, plutôt que silencieusement perdu.
- Après trois tentatives échouées, la ligne passe en statut « échec définitif » et déclenche une alerte à l'équipe.

| Approche | Impact visiteur en cas d'incident HubSpot | Traçabilité des échecs |
| --- | --- | --- |
| Appel synchrone direct | Écran de chargement, parfois erreur visible | Aucune, donnée perdue |
| File d'attente + wp-cron | Aucun, confirmation immédiate | Complète, rejouable |

> Un webhook qui fonctionne neuf fois sur dix n'est pas fiable : il est simplement invisible dans son échec la dixième fois, jusqu'à ce que quelqu'un s'en aperçoive trop tard.

## En résumé

Découpler la soumission du formulaire de l'appel réel à l'API HubSpot, via une file d'attente locale traitée par `wp-cron` avec recul exponentiel, élimine le principal point de fragilité observé sur l'architecture initiale. Le coût est modeste (une table supplémentaire, une tâche planifiée), le gain en fiabilité largement supérieur à ce coût sur un flux de génération de leads où chaque contact perdu a une valeur commerciale réelle.
