Envoyer des données par webhook
Cette page explique exactement quoi envoyer a DURUM.ai pour que l'app fonctionne à son plein potentiel. Que vous utilisiez GHL, Typeform, Calendly, Stripe, Zoho, Pipedrive ou un outil custom, les règles sont les mêmes.
Les 3 choses absolument essentielles
Sans ces 3 éléments, votre événement ne sera pas traite correctement.
1. Identifier votre compte (client_key)
Chaque webhook doit être associé à votre compte. Il y a deux facons de le faire :
Option A -- Dans l'URL avec bid (recommande) :
https://app.durum.ai/api/webhook/lead?bid=VOTRE_BUSINESS_IDC'est le format que l'app vous donne dans Reglages -> Intégrations. Le bid est un identifiant unique impossible a deviner, donc plus sur qu'une clé lisible. Les 7 endpoints standard l'acceptent.
Option B -- Dans l'URL avec client_key :
https://app.durum.ai/api/webhook/lead?client_key=VOTRE_CLEToujours supporte, aucune depreciation.
Option C -- Dans le payload JSON :
{ "client_key": "VOTRE_CLE", "email": "..." }Sans client_key
L'événement est mis en quarantaine. Il ne sera pas perdu, mais il devra être classe manuellement par l'agence. Vos KPIs ne seront pas à jour en temps réel.
2. Identifier le contact (email ou téléphone)
DURUM.ai doit pouvoir identifier qui est le prospect. Envoyez au minimum un de ces champs :
| Champ | Exemple | Priorite |
|---|---|---|
email | jean.tremblay@email.com | Idéal -- sert aussi à la déduplication et à l'attribution |
phone | +15145551234 | Bon backup -- format E.164 ou 10 chiffres |
name | Jean Tremblay | Utile mais insuffisant seul |
Sans email ni téléphone
Pour booking, no-show et sale (envoi JSON generique), l'événement est refusé (réponse rejected: missing_contact_info) : un rendez-vous ou une vente impossible a rattacher à un contact ne sert a rien et fausserait vos KPIs. Pour lead et application, l'événement sera enregistre, mais le système ne pourra pas :
- Détecter les doublons (risque de compter 2 fois le même lead)
- Relier le lead à ses futures actions (booking, vente)
- Enrichir les données avec l'historique du contact
3. Envoyer au bon endpoint
Chaque type d'événement à son propre endpoint. C'est ce qui determine le type dans DURUM.ai.
| Quand | Endpoint | Ce qui est créé |
|---|---|---|
| Un prospect montre de l'intérêt | /api/webhook/lead | Lead |
| Un prospect remplit un formulaire détaillé | /api/webhook/application | Application |
| Un rendez-vous est réservé | /api/webhook/booking | Booking |
| Un prospect ne se présente pas | /api/webhook/no-show | No-show |
| Une vente est conclue | /api/webhook/sale | Vente |
| Un paiement est reçu | /api/webhook/payment | Paiement |
| Un remboursement est émis | /api/webhook/refund | Remboursement |
| Un événement a vous (hors des 7 standard) | /api/webhook/event | Événement custom (nomme) |
Événements custom (vos propres événements)
En plus des 7 événements standard, vous pouvez envoyer n'importe quel événement, sous le nom que vous voulez (« Demo complétée », « Deal deplace en colonne X », « Webinaire regarde a 80% »...).
Le nom de l'événement se met dans l'URL, vous n'envoyez que les données du contact dans le corps :
POST https://app.durum.ai/api/webhook/event?client_key=VOTRE_CLE&event_name=demo_completee&source=custom
Content-Type: application/json
{
"email": "client@exemple.com",
"phone": "+15145550199",
"value": 250,
"occurred_at": "2026-07-07T14:00:00Z"
}Champs reconnus : email, phone, value, occurred_at, contact_id, calendar_name — plus les 13 champs d'attribution si vous voulez rattacher l'événement à une publicité. Tout autre champ est conserve dans le payload brut.
Ou créer un événement custom dans l'app
Reglages -> Événements -> « Ajouter un événement custom » : nommez-le, obtenez son URL de webhook dédiée + le cadre JSON. Il apparaît ensuite dans vos Conversions (page Conversions) et vos mappings d'intégration (ex. « ce calendrier Calendly = cet événement »).
Un événement != trois événements
Si 3 intégrations envoient toutes des leads, c'est un seul événement Lead alimente par 3 sources — pas 3 événements. Pour distinguer par source, utilisez un filtre au niveau de la conversion, jamais un événement separe. Un événement custom = seulement un signal qui n'existe pas dans les 7 standard.
Les 13 champs d'attribution publicitaire
Ces champs permettent a DURUM.ai de relier chaque lead, booking et vente à la publicité exacte qui l'a généré. C'est ce qui alimente le tableau Marketing, le calcul du CPA, ROAS, ROI, et toute l'attribution dans l'app.
Sans ces champs, l'app perd sa valeur principale
Un lead sans UTMs arrive dans DURUM.ai mais il est impossible de savoir de quelle publicité il vient. Vos KPIs marketing (CPA, ROAS, ROI) seront fausses. Vous ne saurez pas quel ad performe et lequel gaspille votre budget.
Les 5 UTMs (noms des éléments publicitaires)
Ce sont les noms lisibles de vos campagnes, publicités et adsets. DURUM.ai les utilise pour matcher les events avec vos publicités Meta.
| # | Champ | Alias acceptes | Ce que ca fait dans l'app | Exemple | Priorite |
|---|---|---|---|---|---|
| 1 | utm_campaign | utmcampaign, UTM_Campaign, campaign_name | Lie l'event à la campagne. Utilise dans le tableau Marketing, le filtre par campagne, et le calcul du spend par campagne. | bootcamp-mars-2026 | Non negociable |
| 2 | utm_content | utmcontent, UTM_Content, ad_name, utm_ad_name | Lie l'event à la publicité (ad). C'est LE champ qui connecte un lead à un ad dans Meta. Sans lui, pas d'attribution par ad. | ad-temoignage-tx1-ix2 | Non negociable |
| 3 | utm_source | utmsource, UTM_Source | Identifié la source du trafic. Permet de distinguer Meta vs Google vs organique dans les rapports. | meta, google, tiktok, organic | Important |
| 4 | utm_medium | utmmedium, UTM_Medium | Identifié le type de trafic. Distingue le paid du organique, de l'email, du social. | paid, cpc, email, social, organic | Important |
| 5 | utm_term | utmterm, UTM_Term, utm_adset_name, adset_name | Lie l'event à l'adset. Permet l'analyse de performance par audience/adset. | adset-lookalike-1pct, adset-retarget-30j | Recommande |
Les 3 IDs Meta (identifiants numériques)
Ce sont les IDs numériques de Meta Ads. Ils permettent un match exact au lieu de matcher par nom (plus fiable si vous renommez une campagne).
| # | Champ | Alias acceptes | Ce que ca fait dans l'app | Exemple | Priorite |
|---|---|---|---|---|---|
| 6 | utm_campaign_id | campaign_id | Match exact avec la campagne Meta par ID au lieu du nom | 120210987654321 | Recommande |
| 7 | utm_ad_id | ad_id | Match exact avec la publicité Meta par ID au lieu du nom | 120210123456789 | Recommande |
| 8 | utm_adset_id | adset_id | Match exact avec l'adset Meta par ID au lieu du nom | 120210111222333 | Recommande |
Noms vs IDs -- pourquoi les deux?
Les noms (utm_campaign, utm_content, utm_term) sont lisibles et utiles dans les tableaux. Les IDs (utm_campaign_id, utm_ad_id, utm_adset_id) sont fiables même si vous renommez une campagne. Idéalement, envoyez les deux. Si vous ne pouvez envoyer qu'un seul, envoyez les noms.
Les 2 champs de tracking avance
Pour l'attribution first-party et les outils tiers comme Hyros.
| # | Champ | Ce que ca fait dans l'app | Exemple | Priorite |
|---|---|---|---|---|
| 9 | fbc_id | Facebook Click ID -- Identifiant unique du clic publicitaire Meta. Permet l'attribution même si les cookies sont bloqués. Capture par le paramètre fbclid dans l'URL. | fb.1.1612345678.abc123def456 | Optionnel (bonus) |
| 10 | h_ad_id | Hyros Ad ID -- Si vous utilisez Hyros pour le tracking, cet ID permet le lien direct. | 120210123456789 | Optionnel (si Hyros) |
Les 3 champs complementaires
| # | Champ | Alias acceptes | Ce que ca fait dans l'app | Exemple | Priorite |
|---|---|---|---|---|---|
| 11 | product_key | product, funnel_key | Identifié le produit ou service. Alimente le funnel par produit, les revenus par produit, et les commissions. | bootcamp_ai | Important |
| 12 | assigned_user_name | assigned_to, Deal_Owner, rep_name | Identifié le rep de vente assigne. Alimente le tableau Ventes Reps et les commissions par rep. | Marie Dupont | Recommande |
| 13 | amount | value, total, Amount, Grand_Total | Montant en dollars de la vente ou du paiement. Sans ce champ, le ROI, ROAS et les revenus sont a $0. | 2500 | Non negociable (ventes/paiements) |
Produits reconnus automatiquement
bootcamp_ai, scaler_vos_ventes, propulsion, club_prive, mastermind_ai, formation_croisiere, accelerateur, acquisition_entreprises. Tout autre nom est accepte et converti en slug (ex : Mon Produit devient mon_produit).
Comment configurer les UTMs dans Meta Ads
Quand vous créez une publicité Meta, configurez les paramètres URL au niveau de la publicité (Ad) :
URL Parameters (dans le champ "URL Parameters" de l'ad) :
utm_source=meta&utm_medium=paid&utm_campaign={campaign.name}&utm_content={ad.name}&utm_term={adset.name}&utm_campaign_id={campaign.id}&utm_ad_id={ad.id}&utm_adset_id={adset.id}Dans Meta Ads, les variables dynamiques entre accolades ({campaign.name}, {ad.name}, etc.) sont remplacées automatiquement par les vraies valeurs. Vos outils (GHL, Typeform, Calendly) capturent ensuite ces paramètres.
Ou placer les UTMs selon votre outil
| Outil | Ou les mettre | Détail |
|---|---|---|
| GHL | Custom fields du contact | utm_source, utm_campaign, utm_content, utm_term, utm_ad_id, utm_campaign_id, utm_adset_id dans le tableau customFields |
| Typeform | Hidden fields du formulaire | Injectes via l'URL du formulaire (#utm_source=meta&utm_campaign=...) |
| Calendly | Paramètres UTM de la page | Calendly capture automatiquement les UTMs de la page ou le lien est affiche |
| Stripe | Metadata du checkout | client_key, product_key, utm_campaign, utm_content dans l'objet metadata |
| Zoho CRM | Champs du deal | UTM_Source, UTM_Campaign, UTM_Content, UTM_Term, utm_ad_id, utm_campaign_id |
| Zoho Books | Custom fields (cf_*) | cf_product_key, cf_source, cf_referent |
| Generique / Make / Zapier | Champs directs dans le JSON | Tous les champs au premier niveau du payload |
Exemple complet avec TOUS les champs d'attribution
{
"email": "jean.tremblay@email.com",
"phone": "+15145551234",
"name": "Jean Tremblay",
"product_key": "bootcamp_ai",
"amount": 2500,
"assigned_user_name": "Marie Dupont",
"utm_source": "meta",
"utm_medium": "paid",
"utm_campaign": "bootcamp-mars-2026",
"utm_content": "ad-temoignage-tx1-ix2",
"utm_term": "adset-lookalike-1pct",
"utm_campaign_id": "120210987654321",
"utm_ad_id": "120210123456789",
"utm_adset_id": "120210111222333",
"fbc_id": "fb.1.1612345678.abc123def456",
"h_ad_id": "120210123456789"
}Les réponses aux questions de vos formulaires
Chaque client pose des questions différentes dans ses formulaires (GHL, Typeform, Calendly). Toutes les réponses sont capturees et stockées, même si elles ne correspondent pas à des champs standard. Rien n'est perdu.
Ce qui est stocke
Toutes les réponses arrivent dans le champ raw_payload de l'événement (format JSONB). C'est un stockage complet du payload original -- chaque question, chaque réponse, chaque champ custom.
En plus du stockage brut, certains champs sont extraits automatiquement vers des colonnes dédiées (email, téléphone, nom, UTMs). Les autres restent dans raw_payload et sont accessibles pour des analyses, des exports ou des filtres avances.
Typeform -- toutes les réponses du formulaire
Chaque réponse est capturee avec la question posee (le titre du champ) et la réponse donnée, quel que soit le type.
Exemples de questions custom que vos clients posent dans leurs formulaires Typeform :
| Question dans le formulaire | Type | Exemple de réponse | Stocke dans |
|---|---|---|---|
| Quel est votre chiffre d'affaires annuel? | number | 500000 | raw_payload |
| Quel est votre secteur d'activité? | choice | E-commerce | raw_payload |
| Quelles plateformes publicitaires utilisez-vous? | choices | ["Facebook Ads", "Google Ads"] | raw_payload |
| Quel est votre objectif principal? | text | Doubler mes ventes en 6 mois | raw_payload |
| Combien d'employes avez-vous? | number | 15 | raw_payload |
| Avez-vous un site web? | url | https://mon-entreprise.com | raw_payload |
| Date souhaitee pour commencer? | date | 2026-04-15 | raw_payload |
| Acceptez-vous d'être contacte? | boolean | true | raw_payload |
| Votre courriel | email | jean@email.com | raw_payload + contact_email |
| Votre téléphone | phone_number | +15145551234 | raw_payload + contact_phone |
| Votre nom complet | text (si le titre contient "nom") | Jean Tremblay | raw_payload + contact_name |
Structure dans le payload -- chaque réponse est stockée comme ceci :
{
"field_ref": {
"label": "Quel est votre chiffre d'affaires annuel?",
"type": "number",
"value": 500000
}
}Vous pouvez ajouter autant de questions que vous voulez dans votre Typeform. Elles seront toutes la.
GHL -- tous les custom fields du contact
GHL envoie les custom fields dans un tableau customFields. Tout le tableau est lu et stocke.
Les champs UTM et produit sont extraits vers des colonnes dédiées (voir section précédente). Tous les autres custom fields sont conserves dans raw_payload.
Exemples de custom fields que vos clients configurent dans GHL :
| Custom Field GHL | Description | Exemple |
|---|---|---|
ville | Ville du prospect | Montreal |
entreprise | Nom de l'entreprise | Tremblay Inc. |
chiffre_affaires | CA annuel | 500000 |
nb_employes | Taille de l'équipe | 15 |
budget_mensuel | Budget marketing | 5000 |
secteur_activite | Secteur | E-commerce |
objectif_principal | Objectif | Augmenter mes ventes de 50% |
comment_avez_vous_entendu | Source déclarée | Publicite Facebook |
qualification_score | Score attribue par le rep | 8 |
revenue_attendu | Revenue estime de la vente | 2500 |
type_entreprise | B2B, B2C, D2C | B2C |
experience_pub | Expérience publicitaire | Debutant |
outil_crm_actuel | CRM utilise | HubSpot |
delai_decision | Urgence | Ce mois-ci |
Comment les envoyer :
{
"contact": {
"email": "jean@email.com",
"customFields": [
{ "field_name": "ville", "value": "Montreal" },
{ "field_name": "entreprise", "value": "Tremblay Inc." },
{ "field_name": "chiffre_affaires", "value": "500000" },
{ "field_name": "nb_employes", "value": "15" },
{ "field_name": "objectif_principal", "value": "Augmenter mes ventes" },
{ "field_name": "budget_mensuel", "value": "5000" },
{ "field_name": "qualification_score", "value": "8" }
]
}
}Pas de limite
Vous pouvez ajouter autant de custom fields que nécessaire. Il n'y a pas de liste predeterminee -- tout ce que vous envoyez dans customFields est stocke et accessible.
Calendly -- les questions de réservation
Calendly permet de poser des questions custom quand quelqu'un réservé un rendez-vous. Elles arrivent dans le tableau questions_and_answers.
Exemples de questions configurées dans Calendly :
| Question Calendly | Réponse type |
|---|---|
| Quel est le nom de votre entreprise? | Tremblay Inc. |
| Quel est votre chiffre d'affaires annuel? | 500 000$ |
| Quel est votre objectif principal pour cet appel? | Valider si le bootcamp est pour moi |
| Comment avez-vous entendu parler de nous? | Publicite Facebook |
| Nombre d'employes? | 15 |
| Avez-vous déjà fait de la publicité en ligne? | Oui, Facebook Ads depuis 6 mois |
| Quel est votre budget mensuel en publicité? | 3000-5000$ |
Structure dans le payload Calendly :
{
"questions_and_answers": [
{
"question": "Quel est le nom de votre entreprise?",
"answer": "Tremblay Inc."
},
{
"question": "Quel est votre chiffre d'affaires annuel?",
"answer": "500 000$"
},
{
"question": "Objectif principal pour cet appel?",
"answer": "Valider si le bootcamp est pour moi"
}
]
}Zoho Books -- custom fields et line items
Zoho Books envoie les custom fields (prefixes cf_) et les line items (produits factures).
| Custom Field Zoho | Description |
|---|---|
cf_product_key | Identifiant du produit |
cf_source | Source de la vente (DURUM, ORGANIC) |
cf_referent | Vendeur/referent |
cf_af_contact_id | ID GHL du contact (via ActiveFunnel) |
Tout autre cf_* | Stocke dans raw_payload |
Les line items (produits factures) sont aussi captures :
{
"line_items": [
{
"name": "Bootcamp IA - Session Mars 2026",
"rate": 2500,
"quantity": 1,
"item_total": 2500
},
{
"name": "Materiel supplementaire",
"rate": 200,
"quantity": 1,
"item_total": 200
}
]
}Le système identifié automatiquement le produit principal (item avec le plus gros montant, en excluant les frais bancaires comme Stripe Processing Fees ou Compte Stripe).
Generique / Make / Zapier -- champs libres
Si vous construisez le JSON vous-même, ajoutez vos champs custom directement dans le payload. Ils seront tous stockés :
{
"email": "jean@email.com",
"client_key": "avego",
"utm_campaign": "bootcamp-mars-2026",
"entreprise": "Tremblay Inc.",
"chiffre_affaires": 500000,
"nb_employes": 15,
"secteur": "E-commerce",
"objectif": "Augmenter mes ventes de 50%",
"budget_pub": 5000,
"source_declaree": "Publicite Facebook",
"questions_custom": {
"experience_pub": "Facebook Ads depuis 6 mois",
"outil_actuel": "HubSpot",
"delai": "Ce mois-ci"
}
}Tout est conserve dans raw_payload. Les champs standard (email, utm_campaign, etc.) sont en plus extraits vers des colonnes dédiées.
Par type d'événement -- quoi envoyer
Lead ou Application
Un prospect montre de l'intérêt ou remplit un formulaire.
POST /api/webhook/lead?client_key=VOTRE_CLE
Content-Type: application/json{
"email": "jean.tremblay@email.com",
"phone": "+15145551234",
"name": "Jean Tremblay",
"product_key": "bootcamp_ai",
"utm_source": "meta",
"utm_campaign": "bootcamp-mars-2026",
"utm_content": "ad-temoignage-tx1"
}| Champ | Statut | Pourquoi |
|---|---|---|
email | Essentiel | Identifié le contact et permet la déduplication |
utm_campaign | Essentiel | Attribution à la campagne |
utm_content | Essentiel | Attribution à la publicité |
phone | Recommande | Backup d'identification + enrichissement |
name | Recommande | Affichage dans les tableaux |
product_key | Recommande | Classification par produit/funnel |
utm_source | Recommande | Source du trafic |
utm_ad_id | Optionnel | Lien direct avec Meta |
Booking (rendez-vous)
Un prospect réservé un rendez-vous.
POST /api/webhook/booking?client_key=VOTRE_CLE{
"email": "jean.tremblay@email.com",
"name": "Jean Tremblay",
"calendar_name": "Appel Decouverte",
"booking_date": "2026-04-05",
"booking_time": "14:00",
"assigned_user_name": "Marie Dupont",
"utm_campaign": "bootcamp-mars-2026",
"utm_content": "ad-temoignage-tx1"
}| Champ | Statut | Pourquoi |
|---|---|---|
email | Essentiel | Relie le booking au lead original |
booking_date | Essentiel | Distingue "date de création" vs "date du RDV" |
booking_time | Recommande | Analyse des creneaux no-show |
calendar_name | Recommande | Type de rendez-vous dans les tableaux |
assigned_user_name | Recommande | Associé au rep pour le suivi de performance |
| UTMs | Important | Si pas déjà sur le lead, c'est la dernière chance de les capturer |
Booking date vs event date
DURUM.ai distingue la date de création du booking (quand le prospect a réservé) de la date du rendez-vous (quand le meeting aura lieu). C'est critique pour calculer correctement les taux de no-show et le pipeline.
No-show
Un prospect ne se présente pas à son rendez-vous.
POST /api/webhook/no-show?client_key=VOTRE_CLE{
"email": "jean.tremblay@email.com",
"name": "Jean Tremblay",
"calendar_name": "Appel Decouverte",
"booking_date": "2026-04-05",
"booking_time": "14:00"
}Vente
Un deal est conclu (gagne ou perdu).
POST /api/webhook/sale?client_key=VOTRE_CLE{
"email": "jean.tremblay@email.com",
"name": "Jean Tremblay",
"amount": 2500,
"product_key": "bootcamp_ai",
"status": "won",
"assigned_user_name": "Marie Dupont",
"utm_campaign": "bootcamp-mars-2026",
"utm_content": "ad-temoignage-tx1"
}| Champ | Statut | Pourquoi |
|---|---|---|
email | Essentiel | Relie la vente au lead et au booking |
amount | Essentiel | Calcul du revenu, ROI, ticket moyen |
status | Important | won = vente, lost = perdue. Par défaut : vente. |
product_key | Recommande | Revenu par produit, commissions |
assigned_user_name | Recommande | Performance par rep |
| UTMs | Important | Attribution de la vente à la publicité |
Sans montant
La vente apparaît dans le funnel mais avec $0. Le ROI, le ROAS et les revenus seront fausses.
Paiement
Un paiement est reçu (facture payee).
POST /api/webhook/payment?client_key=VOTRE_CLE{
"email": "jean.tremblay@email.com",
"name": "Jean Tremblay",
"amount": 2500,
"product_key": "bootcamp_ai",
"invoice_number": "INV-001234",
"payment_status": "paid"
}| Champ | Statut | Pourquoi |
|---|---|---|
email | Essentiel | Relie le paiement au client |
amount | Essentiel | Montant encaisse |
invoice_number | Recommande | Déduplication (évité de compter 2 fois si le webhook est renvoye) |
payment_status | Recommande | paid = encaisse, overdue = en retard |
product_key | Recommande | Revenu par produit |
Remboursement
POST /api/webhook/refund?client_key=VOTRE_CLE{
"email": "jean.tremblay@email.com",
"amount": 2500
}Recapitulatif -- hiérarchie d'importance
| Niveau | Champs | Impact si absent |
|---|---|---|
| Non negociable | client_key (URL ou payload) | Événement en quarantaine, pas dans vos KPIs |
| Non negociable | email ou phone | Pas de déduplication, pas de lien entre les étapes du funnel |
| Non negociable | utm_campaign + utm_content | Pas d'attribution. Impossible de savoir quel ad a généré le lead/vente. CPA, ROAS et ROI non calculables. |
| Non negociable | amount (ventes et paiements) | Revenus a $0, ROI et ROAS fausses |
| Important | utm_source + utm_medium | Impossible de distinguer Meta vs Google vs organique |
| Important | product_key | Pas de funnel par produit, pas de commissions par produit |
| Recommande | utm_term | Pas d'analyse par adset/audience |
| Recommande | utm_campaign_id + utm_ad_id + utm_adset_id | Match par nom au lieu d'ID exact (fragile si vous renommez) |
| Recommande | name, assigned_user_name | Moins de détails dans les tableaux, pas de perf par rep |
| Bonus | fbc_id, h_ad_id | Attribution avancee first-party et Hyros |
Ce que DURUM.ai fait automatiquement
Vous n'avez pas besoin d'envoyer toutes les données dans chaque webhook. Le système fait un travail important en arriere-plan.
Enrichissement UTM automatique
Si un webhook arrive sans UTMs (par exemple un paiement Zoho Books), DURUM.ai cherche automatiquement les UTMs dans cet ordre :
- Custom fields GHL -- Si le contact existe dans GHL avec des champs UTM configurés, ils sont recuperes
- Tracker navigateur -- Si le prospect a visite votre site avec le tracker DURUM.ai installe, ses UTMs ont été captures automatiquement (premier clic)
- Events précédents -- Si le même email a déjà un lead ou un booking avec des UTMs, ils sont herites
Concretement : si un prospect remplit un formulaire avec les bons UTMs, puis réservé un booking, puis paie via Stripe -- même si Stripe n'envoie aucun UTM, la vente sera attribuee à la bonne publicité grace au lead original.
TIP
C'est pourquoi les UTMs sur le premier point de contact (lead ou application) sont si importants. Même si les étapes suivantes n'en ont pas, l'attribution fonctionnera.
Déduplication intelligente
DURUM.ai empeche automatiquement les doublons. Vous n'avez pas a vous en soucier si un webhook est envoye 2 fois.
| Source | Méthode de dedup | Ce qui est vérifié |
|---|---|---|
| Typeform | Token de soumission | Chaque soumission à un token unique |
| Calendly | URI de l'invite | Chaque invite à une URI unique |
| Zoho Books | Numéro de facture | Même invoice_number = même événement |
| GHL / Generique | Email + type + fenêtre temps | Même email + même type dans les 24h (leads) ou 2 min (autres) |
Rep assigne automatiquement
Si vous n'envoyez pas le nom du rep (assigned_user_name), le système le cherche automatiquement :
- Dans le contact GHL (champ
assigned_to) - Dans les events précédents du même contact
- Via le ID utilisateur GHL mappe à un rep dans DURUM.ai
Client identifié automatiquement
Si le client_key n'est pas dans l'URL, le système tente de l'identifier via :
- L'email du contact (s'il existe déjà dans un compte)
- Le numéro de téléphone
- L'ID de contact GHL
- Le numéro de facture
- L'ID client Zoho ou Stripe Connect
Alias acceptes par champ
DURUM.ai accepte plusieurs noms pour le même champ. Utilisez celui qui correspond à votre outil.
| Ce que DURUM.ai stocke | Noms acceptes |
|---|---|
contact_email | email, contact_email, customer_email |
contact_phone | phone, contact_phone, customer_phone |
contact_name | name, contact_name, customer_name |
client_key | client_key, clientKey |
product_key | product_key, product, funnel_key |
utm_source | utm_source, utmsource, UTM_Source |
utm_campaign | utm_campaign, utmcampaign, UTM_Campaign |
utm_content | utm_content, utmcontent, UTM_Content |
utm_term | utm_term, utmterm, UTM_Term, utm_adset_name |
meta_ad_id | utm_ad_id, ad_id |
meta_campaign_id | utm_campaign_id, campaign_id |
meta_adset_id | utm_adset_id, adset_id |
value (montant) | amount, value, total, Amount, Grand_Total |
assigned_user_name | assigned_user_name, assigned_to, Deal_Owner, rep_name |
Specificites par plateforme
Chaque outil envoie les données dans un format différent. Voici ou placer les champs importants selon votre outil.
GHL (GoHighLevel)
Les UTMs et le produit vont dans les custom fields du contact :
{
"contact": {
"email": "jean@email.com",
"customFields": [
{ "field_name": "utm_campaign", "value": "bootcamp-mars-2026" },
{ "field_name": "utm_content", "value": "ad-temoignage-tx1" },
{ "field_name": "product_key", "value": "bootcamp_ai" }
]
}
}L'URL doit inclure ?client_key=VOTRE_CLE&source=ghl.
Vous pouvez ajouter n'importe quel custom field supplementaire (ville, entreprise, chiffre_affaires, etc.). Ils seront tous conserves dans les données brutes.
:::détails Voir le payload GHL complet (lead)
{
"contact": {
"id": "abc123",
"firstName": "Jean",
"lastName": "Tremblay",
"email": "jean.tremblay@email.com",
"phone": "+15145551234",
"dateAdded": "2026-03-30T14:30:00.000Z",
"tags": ["lead", "bootcamp-ai"],
"assignedTo": "rep-user-id",
"customFields": [
{ "field_name": "utm_source", "value": "meta" },
{ "field_name": "utm_campaign", "value": "bootcamp-mars-2026" },
{ "field_name": "utm_content", "value": "ad-temoignage-tx1" },
{ "field_name": "utm_term", "value": "adset-lookalike-1pct" },
{ "field_name": "utm_ad_id", "value": "120210123456789" },
{ "field_name": "utm_campaign_id", "value": "120210987654321" },
{ "field_name": "product_key", "value": "bootcamp_ai" },
{ "field_name": "ville", "value": "Montreal" },
{ "field_name": "entreprise", "value": "Tremblay Inc." }
]
},
"locationId": "ghl-location-abc123",
"form": {
"id": "form-xyz-789",
"name": "Formulaire Bootcamp IA"
}
}:::
:::détails Voir le payload GHL complet (booking)
{
"id": "appt-abc123",
"contactId": "contact-xyz789",
"contact": {
"email": "jean.tremblay@email.com",
"name": "Jean Tremblay",
"customFields": [
{ "field_name": "utm_campaign", "value": "bootcamp-mars-2026" }
]
},
"calendarName": "Appel Decouverte",
"startTime": "2026-04-05T14:00:00.000Z",
"appointmentStatus": "scheduled",
"assignedUserId": "user-rep-123",
"locationId": "ghl-location-abc123"
}:::
:::détails Voir le payload GHL complet (vente)
{
"opportunity": {
"id": "opp-abc123",
"status": "won",
"monetaryValue": 2500
},
"contact": {
"id": "contact-xyz789",
"email": "jean.tremblay@email.com",
"name": "Jean Tremblay",
"customFields": [
{ "field_name": "product_key", "value": "bootcamp_ai" }
]
},
"locationId": "ghl-location-abc123"
}:::
Typeform
Les UTMs et le client_key vont dans les hidden fields du formulaire :
https://form.typeform.com/to/XXXXX#client_key=VOTRE_CLE&utm_source=meta&utm_campaign=bootcamp-mars-2026&utm_content=ad-temoignage-tx1&product_key=bootcamp_aiLes réponses du formulaire (email, téléphone, nom, questions custom) sont extraites automatiquement depuis le tableau answers.
:::détails Voir le payload Typeform complet
{
"form_response": {
"form_id": "sJ5lztlq",
"token": "unique-submission-token",
"definition": { "title": "Formulaire Bootcamp IA" },
"hidden": {
"client_key": "VOTRE_CLE",
"utm_source": "meta",
"utm_campaign": "bootcamp-mars-2026",
"utm_content": "ad-temoignage-tx1",
"utm_ad_id": "120210123456789",
"product_key": "bootcamp_ai"
},
"answers": [
{ "type": "email", "email": "jean@email.com", "field": { "title": "Votre courriel" } },
{ "type": "phone_number", "phone_number": "+15145551234", "field": { "title": "Telephone" } },
{ "type": "text", "text": "Jean Tremblay", "field": { "title": "Votre nom" } },
{ "type": "choice", "choice": { "label": "E-commerce" }, "field": { "title": "Secteur" } },
{ "type": "number", "number": 500000, "field": { "title": "Chiffre d'affaires" } }
]
}
}:::
Calendly
Les UTMs sont captures automatiquement dans l'objet tracking si l'URL de la page de réservation contient les paramètres UTM. Les questions personnalisées sont dans questions_and_answers.
L'URL doit inclure ?client_key=VOTRE_CLE.
:::détails Voir le payload Calendly complet
{
"event": "invitee.created",
"payload": {
"event_type": { "name": "Appel Decouverte" },
"invitee": {
"email": "jean.tremblay@email.com",
"name": "Jean Tremblay",
"text_reminder_number": "+15145551234",
"uri": "https://api.calendly.com/scheduled_events/xxx/invitees/yyy"
},
"scheduled_event": {
"start_time": "2026-04-05T14:00:00.000Z",
"event_memberships": [{ "user_name": "Marie Dupont" }]
},
"tracking": {
"utm_source": "meta",
"utm_campaign": "bootcamp-mars-2026",
"utm_content": "ad-temoignage-tx1"
},
"questions_and_answers": [
{ "question": "Votre entreprise?", "answer": "Tremblay Inc." },
{ "question": "Chiffre d'affaires?", "answer": "500 000$" }
]
}
}:::
Stripe
Le client_key et le product_key vont dans les metadata du checkout session ou de la facture. Les montants sont en cents (divises automatiquement par 100).
Pas besoin de client_key dans l'URL -- Stripe est détecté par sa signature.
:::détails Voir le payload Stripe complet
{
"type": "checkout.session.completed",
"data": {
"object": {
"customer_email": "jean.tremblay@email.com",
"amount_total": 250000,
"payment_status": "paid",
"customer_details": {
"name": "Jean Tremblay",
"email": "jean.tremblay@email.com"
},
"metadata": {
"client_key": "VOTRE_CLE",
"product_key": "bootcamp_ai",
"utm_campaign": "bootcamp-mars-2026"
}
}
}
}:::
Zoho CRM
Les UTMs sont dans des champs du deal (UTM_Source, UTM_Campaign, UTM_Content). Le client_key doit être dans l'URL.
:::détails Voir le payload Zoho CRM complet
{
"data": [{
"Deal_Name": "Jean Tremblay - Bootcamp AI",
"Stage": "Closed Won",
"Amount": 2500,
"Email": "jean.tremblay@email.com",
"Contact_Name": "Jean Tremblay",
"Owner": { "name": "Marie Dupont" },
"Product": "bootcamp_ai",
"UTM_Source": "meta",
"UTM_Campaign": "bootcamp-mars-2026",
"UTM_Content": "ad-temoignage-tx1"
}]
}:::
Zoho Books
Le client_key doit être dans l'URL (Zoho Books ne l'envoie pas dans le payload). Les custom fields Zoho sont prefixes par cf_.
:::détails Voir le payload Zoho Books complet
{
"payment": {
"amount": 2500,
"payment_status": "paid",
"customer_name": "Jean Tremblay",
"customer_email": "jean.tremblay@email.com",
"invoices": [{ "invoice_number": "INV-001234", "amount_applied": 2500 }],
"custom_fields": [
{ "api_name": "cf_client_key", "value": "VOTRE_CLE" },
{ "api_name": "cf_product_key", "value": "bootcamp_ai" },
{ "api_name": "cf_source", "value": "DURUM" },
{ "api_name": "cf_referent", "value": "Marie Dupont" }
]
}
}:::
Pipedrive
L'URL doit inclure ?client_key=VOTRE_CLE&source=pipedrive. Les emails et telephones sont des tableaux avec primary: true.
:::détails Voir le payload Pipedrive complet (deal gagne)
{
"meta": { "action": "updated", "object": "deal", "company_id": 12345 },
"current": {
"title": "Jean Tremblay - Bootcamp IA",
"status": "won",
"value": 2500,
"person_id": {
"name": "Jean Tremblay",
"email": [{ "value": "jean@email.com", "primary": true }],
"phone": [{ "value": "+15145551234", "primary": true }]
},
"user_id": { "name": "Marie Dupont" },
"won_time": "2026-03-30T15:00:00Z"
},
"previous": { "status": "open" }
}:::
Tester votre webhook
Copiez-collez cette commande dans votre terminal pour envoyer un lead de test :
curl -X POST "https://app.durum.ai/api/webhook/lead?client_key=VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{
"email": "test-webhook@votredomaine.com",
"name": "Test Webhook",
"utm_source": "test",
"utm_campaign": "test-webhook",
"utm_content": "test-ad"
}'Puis vérifiez dans DURUM.ai > Logs Data que l'événement apparaît.
Dépannage rapide
| Symptome | Cause | Solution |
|---|---|---|
| Événement absent de l'app | client_key manquant ou invalide | Ajoutez ?client_key=VOTRE_CLE à l'URL |
Statut quarantined | Formulaire ou client non reconnu | Normal pour un nouveau formulaire. L'agence le catégorisé. |
Réponse deduped: true | Événement déjà reçu | Normal. Le doublon a été détecté. |
| UTMs vides dans le dashboard | Pas d'UTMs dans le webhook | Configurez les hidden fields (Typeform), custom fields (GHL) ou tracking (Calendly) |
| Montant a $0 sur une vente | Champ amount absent | Ajoutez le montant dans le payload |
| Réponse 401 | Authentification échouée | Pour GHL : ajoutez &source=ghl. Pour Stripe : vérifiez la signature. |
| Réponse 429 | Trop de requêtes | Maximum 60 par minute par IP |
| Réponse 202 | Serveur temporairement occupe | L'événement est en file d'attente et sera traite sous quelques minutes |