Le contexte
Hexa est une company builder : sous une même marque, plusieurs startups - Roundtable, Panora, Plato et d'autres - chacune avec ses propres offres d'emploi. Toutes ces offres vivent dans Ashby, l'ATS utilisé par les équipes recrutement.
Le problème classique : Ashby propose un job board hébergé chez lui. Pratique, mais limité. Le design ne suit pas la charte du site, les filtres « par startup » n'existent pas en natif, et l'expérience candidat coupe en deux - on quitte hexa.com, on atterrit sur jobs.ashbyhq.com.
L'objectif : tout ramener à l'intérieur du site Webflow. Un seul univers visuel, de la page d'accueil à la candidature. En tant qu'agence webflow technique, c'est un défi intéressant à relever. On vous explique tout dans ce guide.
L'architecture, en trois briques
Trois éléments, et c'est tout :
- Une clé API côté Ashby
- Un Worker Cloudflare qui sert de proxy entre Ashby et Webflow
- Un script vanilla JS posé sur la page Webflow
Pourquoi le Worker au milieu ? On ne peut pas appeler Ashby directement depuis le navigateur. La clé API serait visible dans les outils dev de n'importe quel visiteur, et Ashby refuse de toute façon les requêtes CORS depuis un domaine quelconque. Il faut un intermédiaire, c'est exactement le rôle du Worker.
Étape 1 - La clé API Ashby
Dans Ashby, on génère une clé depuis Settings > API. Le scope minimal pour ce besoin est jobPosting:read uniquement la lecture des offres publiées, rien d'autre. Pas de droits d'écriture, pas d'accès aux candidats : si la clé fuite un jour, le risque est nul.
Cette clé ne quitte jamais le Worker. Elle n'apparaît à aucun endroit dans Webflow ou le navigateur.

Étape 2 - Le Worker Cloudflare
Pourquoi Cloudflare Workers plutôt qu'un backend traditionnel ? Trois raisons concrètes
- Mise en place hyper simple
- 100 000 requêtes par jour offertes - largement suffisant pour un site recrutement
- Latence quasi nulle : le code s'exécute en edge, au plus près du visiteur.
- Le déploiement tient en une commande :
wrangler deploy.
Le Worker fait deux choses, pas une de plus.
- Il sécurise la clé. Elle est stockée comme variable d'environnement dans le tableau de bord Cloudflare. Le code y accède via
c.env.ASHBY_API_KEY. Aucun autre acteur ne peut la lire. - Il restreint les origines. Le CORS est paramétré pour n'autoriser que les domaines déclarés (production + preview Webflow). Si un autre site essaie d'appeler le Worker, la requête est rejetée avant même d'arriver chez Ashby.
Côté code, on utilise Hono - un framework minimaliste pensé pour les Workers. Deux endpoints suffisent :
/api/jobs GET /api/jobs/:idSous le capot, chaque route relaie un appel POST vers api.ashbyhq.com (jobPosting.list et jobPosting.info) avec une authentification Basic, et renvoie la réponse au front sans la transformer. Le Worker reste mince : moins de 60 lignes au total.

Étape 3 - Le rendu sur Webflow
Sur la page Webflow, on construit la structure HTML avec des data-attributes. Une card « template » en brouillon, masquée par défaut, sert de modèle :
div data-jobs="list">
<!-- template, cloné pour chaque offre -->
<div data-jobs="item">
<a data-jobs="link">
<img data-jobs="team-logo" />
<div data-jobs="title"></div>
<div data-jobs="department"></div>
<div data-jobs="location"></div>
<div data-jobs="type"></div>
</a>
</div>
</div>Au chargement de la page, le script appelle /api/jobs sur le Worker, filtre les offres publiées (isListed === true), trie par date de publication descendante, puis clone le template pour chaque offre en remplissant les champs.
Le détail malin : le logo de chaque startup est récupéré depuis une CMS Collection Webflow (« Clients | Logos »), en faisant correspondre le teamName retourné par Ashby à un attribut data-company-name posé sur l'élément Webflow.
Résultat : quand l'équipe Hexa ajoute une nouvelle startup dans Ashby, il suffit d'ajouter son logo dans la CMS - aucune ligne de code à toucher.

Filtres et expérience candidat
Trois filtres sont générés dynamiquement à partir des données
- Une rangée de pills par entreprise, avec logo et compteur d'offres.
- Un sélecteur par entreprise
Un sélecteur par type de contrat (CDI, stage, CDD, etc.) avec libellés français.
Tout est branché sur la même fonction de filtrage. Pas de re-fetch à chaque clic : la liste complète est gardée en mémoire, on filtre côté client. La liste se met à jour instantanément.
La page détail d'une offre
Quand un visiteur clique sur une offre, il atterrit sur une page Webflow /position avec l'ID de l'offre en paramètre :
/position?ashby_jid=Sur cette page, un second script lit l'ID dans l'URL, appelle /api/jobs/:id, et remplit la fiche : titre, description en HTML riche envoyée par Ashby, localisation, fourchette de rémunération si renseignée, et bouton de candidature qui pointe vers le formulaire Ashby.

On a même ajouté un petit skeleton au chargement de la page pour indiquer à l'utilisateur que le contenu charge

Pourquoi pas une autre approche ?
On aurait pu :
- Embarquer le job board iframe d'Ashby - design figé, expérience cassée à chaque navigation, impossible à thématiser.
- Synchroniser les offres dans Airtable puis dans la CMS Webflow via Zapier ou Make - données potentiellement obsolètes, une stack supplémentaire à maintenir, et un coût mensuel récurrent.
Construire un vrai backend (Node + base de données) - du sur-dimensionné pour un job board, et un coût de maintenance disproportionné.
Le combo Worker + script vanilla coûte 0 € par mois pour le volume Hexa, se met à jour en temps réel à chaque modification dans Ashby, et tient dans deux fichiers.
Le résultat final
Une page recrutement 100 % sur la marque Hexa, avec un filtre par startup que les candidats utilisent vraiment, des offres mises à jour automatiquement dès qu'elles sont publiées dans Ashby, et un Worker qui tient sans broncher peu importe le trafic.




