Skip to content

Envoyer des donnees par webhook

Cette page explique exactement quoi envoyer a DURUM.ai pour que l'app fonctionne a son plein potentiel. Que vous utilisiez GHL, Typeform, Calendly, Stripe, Zoho, Pipedrive ou un outil custom, les regles sont les memes.


Les 3 choses absolument essentielles

Sans ces 3 elements, votre evenement ne sera pas traite correctement.

1. Identifier votre compte (client_key)

Chaque webhook doit etre associe a votre compte. Il y a deux facons de le faire :

Option A -- Dans l'URL (recommande) :

https://app.durum.ai/api/webhook/lead?client_key=VOTRE_CLE

Option B -- Dans le payload JSON :

json
{ "client_key": "VOTRE_CLE", "email": "..." }

Sans client_key

L'evenement est mis en quarantaine. Il ne sera pas perdu, mais il devra etre classe manuellement par l'agence. Vos KPIs ne seront pas a jour en temps reel.

2. Identifier le contact (email ou telephone)

DURUM.ai doit pouvoir identifier qui est le prospect. Envoyez au minimum un de ces champs :

ChampExemplePriorite
emailjean.tremblay@email.comIdeal -- sert aussi a la deduplication et a l'attribution
phone+15145551234Bon backup -- format E.164 ou 10 chiffres
nameJean TremblayUtile mais insuffisant seul

Sans email ni telephone

L'evenement sera quand meme enregistre, mais le systeme ne pourra pas :

  • Detecter les doublons (risque de compter 2 fois le meme lead)
  • Relier le lead a ses futures actions (booking, vente)
  • Enrichir les donnees avec l'historique du contact

3. Envoyer au bon endpoint

Chaque type d'evenement a son propre endpoint. C'est ce qui determine le type dans DURUM.ai.

QuandEndpointCe qui est cree
Un prospect montre de l'interet/api/webhook/leadLead
Un prospect remplit un formulaire detaille/api/webhook/applicationApplication
Un rendez-vous est reserve/api/webhook/bookingBooking
Un prospect ne se presente pas/api/webhook/no-showNo-show
Une vente est conclue/api/webhook/saleVente
Un paiement est recu/api/webhook/paymentPaiement
Un remboursement est emis/api/webhook/refundRemboursement

Les 13 champs d'attribution publicitaire

Ces champs permettent a DURUM.ai de relier chaque lead, booking et vente a la publicite exacte qui l'a genere. 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 publicite 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 elements publicitaires)

Ce sont les noms lisibles de vos campagnes, publicites et adsets. DURUM.ai les utilise pour matcher les events avec vos publicites Meta.

#ChampAlias acceptesCe que ca fait dans l'appExemplePriorite
1utm_campaignutmcampaign, UTM_Campaign, campaign_nameLie l'event a la campagne. Utilise dans le tableau Marketing, le filtre par campagne, et le calcul du spend par campagne.bootcamp-mars-2026Non negociable
2utm_contentutmcontent, UTM_Content, ad_name, utm_ad_nameLie l'event a la publicite (ad). C'est LE champ qui connecte un lead a un ad dans Meta. Sans lui, pas d'attribution par ad.ad-temoignage-tx1-ix2Non negociable
3utm_sourceutmsource, UTM_SourceIdentifie la source du trafic. Permet de distinguer Meta vs Google vs organique dans les rapports.meta, google, tiktok, organicImportant
4utm_mediumutmmedium, UTM_MediumIdentifie le type de trafic. Distingue le paid du organique, de l'email, du social.paid, cpc, email, social, organicImportant
5utm_termutmterm, UTM_Term, utm_adset_name, adset_nameLie l'event a l'adset. Permet l'analyse de performance par audience/adset.adset-lookalike-1pct, adset-retarget-30jRecommande

Les 3 IDs Meta (identifiants numeriques)

Ce sont les IDs numeriques de Meta Ads. Ils permettent un match exact au lieu de matcher par nom (plus fiable si vous renommez une campagne).

#ChampAlias acceptesCe que ca fait dans l'appExemplePriorite
6utm_campaign_idcampaign_idMatch exact avec la campagne Meta par ID au lieu du nom120210987654321Recommande
7utm_ad_idad_idMatch exact avec la publicite Meta par ID au lieu du nom120210123456789Recommande
8utm_adset_idadset_idMatch exact avec l'adset Meta par ID au lieu du nom120210111222333Recommande

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 meme si vous renommez une campagne. Idealement, 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.

#ChampCe que ca fait dans l'appExemplePriorite
9fbc_idFacebook Click ID -- Identifiant unique du clic publicitaire Meta. Permet l'attribution meme si les cookies sont bloques. Capture par le parametre fbclid dans l'URL.fb.1.1612345678.abc123def456Optionnel (bonus)
10h_ad_idHyros Ad ID -- Si vous utilisez Hyros pour le tracking, cet ID permet le lien direct.120210123456789Optionnel (si Hyros)

Les 3 champs complementaires

#ChampAlias acceptesCe que ca fait dans l'appExemplePriorite
11product_keyproduct, funnel_keyIdentifie le produit ou service. Alimente le funnel par produit, les revenus par produit, et les commissions.bootcamp_aiImportant
12assigned_user_nameassigned_to, Deal_Owner, rep_nameIdentifie le rep de vente assigne. Alimente le tableau Ventes Reps et les commissions par rep.Marie DupontRecommande
13amountvalue, total, Amount, Grand_TotalMontant en dollars de la vente ou du paiement. Sans ce champ, le ROI, ROAS et les revenus sont a $0.2500Non 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 creez une publicite Meta, configurez les parametres URL au niveau de la publicite (Ad) :

URL Parameters (dans le champ "URL Parameters" de l'ad) :

txt
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 remplacees automatiquement par les vraies valeurs. Vos outils (GHL, Typeform, Calendly) capturent ensuite ces parametres.

Ou placer les UTMs selon votre outil

OutilOu les mettreDetail
GHLCustom fields du contactutm_source, utm_campaign, utm_content, utm_term, utm_ad_id, utm_campaign_id, utm_adset_id dans le tableau customFields
TypeformHidden fields du formulaireInjectes via l'URL du formulaire (#utm_source=meta&utm_campaign=...)
CalendlyParametres UTM de la pageCalendly capture automatiquement les UTMs de la page ou le lien est affiche
StripeMetadata du checkoutclient_key, product_key, utm_campaign, utm_content dans l'objet metadata
Zoho CRMChamps du dealUTM_Source, UTM_Campaign, UTM_Content, UTM_Term, utm_ad_id, utm_campaign_id
Zoho BooksCustom fields (cf_*)cf_product_key, cf_source, cf_referent
Generique / Make / ZapierChamps directs dans le JSONTous les champs au premier niveau du payload

Exemple complet avec TOUS les champs d'attribution

json
{
  "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 reponses aux questions de vos formulaires

Chaque client pose des questions differentes dans ses formulaires (GHL, Typeform, Calendly). Toutes les reponses sont capturees et stockees, meme si elles ne correspondent pas a des champs standard. Rien n'est perdu.

Ce qui est stocke

Toutes les reponses arrivent dans le champ raw_payload de l'evenement (format JSONB). C'est un stockage complet du payload original -- chaque question, chaque reponse, chaque champ custom.

En plus du stockage brut, certains champs sont extraits automatiquement vers des colonnes dediees (email, telephone, nom, UTMs). Les autres restent dans raw_payload et sont accessibles pour des analyses, des exports ou des filtres avances.

Typeform -- toutes les reponses du formulaire

Chaque reponse est capturee avec la question posee (le titre du champ) et la reponse donnee, quel que soit le type.

Exemples de questions custom que vos clients posent dans leurs formulaires Typeform :

Question dans le formulaireTypeExemple de reponseStocke dans
Quel est votre chiffre d'affaires annuel?number500000raw_payload
Quel est votre secteur d'activite?choiceE-commerceraw_payload
Quelles plateformes publicitaires utilisez-vous?choices["Facebook Ads", "Google Ads"]raw_payload
Quel est votre objectif principal?textDoubler mes ventes en 6 moisraw_payload
Combien d'employes avez-vous?number15raw_payload
Avez-vous un site web?urlhttps://mon-entreprise.comraw_payload
Date souhaitee pour commencer?date2026-04-15raw_payload
Acceptez-vous d'etre contacte?booleantrueraw_payload
Votre courrielemailjean@email.comraw_payload + contact_email
Votre telephonephone_number+15145551234raw_payload + contact_phone
Votre nom complettext (si le titre contient "nom")Jean Tremblayraw_payload + contact_name

Structure dans le payload -- chaque reponse est stockee comme ceci :

json
{
  "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 dediees (voir section precedente). Tous les autres custom fields sont conserves dans raw_payload.

Exemples de custom fields que vos clients configurent dans GHL :

Custom Field GHLDescriptionExemple
villeVille du prospectMontreal
entrepriseNom de l'entrepriseTremblay Inc.
chiffre_affairesCA annuel500000
nb_employesTaille de l'equipe15
budget_mensuelBudget marketing5000
secteur_activiteSecteurE-commerce
objectif_principalObjectifAugmenter mes ventes de 50%
comment_avez_vous_entenduSource declareePublicite Facebook
qualification_scoreScore attribue par le rep8
revenue_attenduRevenue estime de la vente2500
type_entrepriseB2B, B2C, D2CB2C
experience_pubExperience publicitaireDebutant
outil_crm_actuelCRM utiliseHubSpot
delai_decisionUrgenceCe mois-ci

Comment les envoyer :

json
{
  "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 necessaire. Il n'y a pas de liste predeterminee -- tout ce que vous envoyez dans customFields est stocke et accessible.

Calendly -- les questions de reservation

Calendly permet de poser des questions custom quand quelqu'un reserve un rendez-vous. Elles arrivent dans le tableau questions_and_answers.

Exemples de questions configurees dans Calendly :

Question CalendlyReponse 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 deja fait de la publicite en ligne?Oui, Facebook Ads depuis 6 mois
Quel est votre budget mensuel en publicite?3000-5000$

Structure dans le payload Calendly :

json
{
  "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 ZohoDescription
cf_product_keyIdentifiant du produit
cf_sourceSource de la vente (DURUM, ORGANIC)
cf_referentVendeur/referent
cf_af_contact_idID GHL du contact (via ActiveFunnel)
Tout autre cf_*Stocke dans raw_payload

Les line items (produits factures) sont aussi captures :

json
{
  "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 systeme identifie 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-meme, ajoutez vos champs custom directement dans le payload. Ils seront tous stockes :

json
{
  "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 dediees.


Par type d'evenement -- quoi envoyer

Lead ou Application

Un prospect montre de l'interet ou remplit un formulaire.

POST /api/webhook/lead?client_key=VOTRE_CLE
Content-Type: application/json
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"
}
ChampStatutPourquoi
emailEssentielIdentifie le contact et permet la deduplication
utm_campaignEssentielAttribution a la campagne
utm_contentEssentielAttribution a la publicite
phoneRecommandeBackup d'identification + enrichissement
nameRecommandeAffichage dans les tableaux
product_keyRecommandeClassification par produit/funnel
utm_sourceRecommandeSource du trafic
utm_ad_idOptionnelLien direct avec Meta

Booking (rendez-vous)

Un prospect reserve un rendez-vous.

POST /api/webhook/booking?client_key=VOTRE_CLE
json
{
  "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"
}
ChampStatutPourquoi
emailEssentielRelie le booking au lead original
booking_dateEssentielDistingue "date de creation" vs "date du RDV"
booking_timeRecommandeAnalyse des creneaux no-show
calendar_nameRecommandeType de rendez-vous dans les tableaux
assigned_user_nameRecommandeAssocie au rep pour le suivi de performance
UTMsImportantSi pas deja sur le lead, c'est la derniere chance de les capturer

Booking date vs event date

DURUM.ai distingue la date de creation du booking (quand le prospect a reserve) 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 presente pas a son rendez-vous.

POST /api/webhook/no-show?client_key=VOTRE_CLE
json
{
  "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
json
{
  "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"
}
ChampStatutPourquoi
emailEssentielRelie la vente au lead et au booking
amountEssentielCalcul du revenu, ROI, ticket moyen
statusImportantwon = vente, lost = perdue. Par defaut : vente.
product_keyRecommandeRevenu par produit, commissions
assigned_user_nameRecommandePerformance par rep
UTMsImportantAttribution de la vente a la publicite

Sans montant

La vente apparait dans le funnel mais avec $0. Le ROI, le ROAS et les revenus seront fausses.

Paiement

Un paiement est recu (facture payee).

POST /api/webhook/payment?client_key=VOTRE_CLE
json
{
  "email": "jean.tremblay@email.com",
  "name": "Jean Tremblay",
  "amount": 2500,
  "product_key": "bootcamp_ai",
  "invoice_number": "INV-001234",
  "payment_status": "paid"
}
ChampStatutPourquoi
emailEssentielRelie le paiement au client
amountEssentielMontant encaisse
invoice_numberRecommandeDeduplication (evite de compter 2 fois si le webhook est renvoye)
payment_statusRecommandepaid = encaisse, overdue = en retard
product_keyRecommandeRevenu par produit

Remboursement

POST /api/webhook/refund?client_key=VOTRE_CLE
json
{
  "email": "jean.tremblay@email.com",
  "amount": 2500
}

Recapitulatif -- hierarchie d'importance

NiveauChampsImpact si absent
Non negociableclient_key (URL ou payload)Evenement en quarantaine, pas dans vos KPIs
Non negociableemail ou phonePas de deduplication, pas de lien entre les etapes du funnel
Non negociableutm_campaign + utm_contentPas d'attribution. Impossible de savoir quel ad a genere le lead/vente. CPA, ROAS et ROI non calculables.
Non negociableamount (ventes et paiements)Revenus a $0, ROI et ROAS fausses
Importantutm_source + utm_mediumImpossible de distinguer Meta vs Google vs organique
Importantproduct_keyPas de funnel par produit, pas de commissions par produit
Recommandeutm_termPas d'analyse par adset/audience
Recommandeutm_campaign_id + utm_ad_id + utm_adset_idMatch par nom au lieu d'ID exact (fragile si vous renommez)
Recommandename, assigned_user_nameMoins de details dans les tableaux, pas de perf par rep
Bonusfbc_id, h_ad_idAttribution avancee first-party et Hyros

Ce que DURUM.ai fait automatiquement

Vous n'avez pas besoin d'envoyer toutes les donnees dans chaque webhook. Le systeme 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 :

  1. Custom fields GHL -- Si le contact existe dans GHL avec des champs UTM configures, ils sont recuperes
  2. Tracker navigateur -- Si le prospect a visite votre site avec le tracker DURUM.ai installe, ses UTMs ont ete captures automatiquement (premier clic)
  3. Events precedents -- Si le meme email a deja un lead ou un booking avec des UTMs, ils sont herites

Concretement : si un prospect remplit un formulaire avec les bons UTMs, puis reserve un booking, puis paie via Stripe -- meme si Stripe n'envoie aucun UTM, la vente sera attribuee a la bonne publicite grace au lead original.

TIP

C'est pourquoi les UTMs sur le premier point de contact (lead ou application) sont si importants. Meme si les etapes suivantes n'en ont pas, l'attribution fonctionnera.

Deduplication intelligente

DURUM.ai empeche automatiquement les doublons. Vous n'avez pas a vous en soucier si un webhook est envoye 2 fois.

SourceMethode de dedupCe qui est verifie
TypeformToken de soumissionChaque soumission a un token unique
CalendlyURI de l'inviteChaque invite a une URI unique
Zoho BooksNumero de factureMeme invoice_number = meme evenement
GHL / GeneriqueEmail + type + fenetre tempsMeme email + meme 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 systeme le cherche automatiquement :

  1. Dans le contact GHL (champ assigned_to)
  2. Dans les events precedents du meme contact
  3. Via le ID utilisateur GHL mappe a un rep dans DURUM.ai

Client identifie automatiquement

Si le client_key n'est pas dans l'URL, le systeme tente de l'identifier via :

  • L'email du contact (s'il existe deja dans un compte)
  • Le numero de telephone
  • L'ID de contact GHL
  • Le numero de facture
  • L'ID client Zoho ou Stripe Connect

Alias acceptes par champ

DURUM.ai accepte plusieurs noms pour le meme champ. Utilisez celui qui correspond a votre outil.

Ce que DURUM.ai stockeNoms acceptes
contact_emailemail, contact_email, customer_email
contact_phonephone, contact_phone, customer_phone
contact_namename, contact_name, customer_name
client_keyclient_key, clientKey
product_keyproduct_key, product, funnel_key
utm_sourceutm_source, utmsource, UTM_Source
utm_campaignutm_campaign, utmcampaign, UTM_Campaign
utm_contentutm_content, utmcontent, UTM_Content
utm_termutm_term, utmterm, UTM_Term, utm_adset_name
meta_ad_idutm_ad_id, ad_id
meta_campaign_idutm_campaign_id, campaign_id
meta_adset_idutm_adset_id, adset_id
value (montant)amount, value, total, Amount, Grand_Total
assigned_user_nameassigned_user_name, assigned_to, Deal_Owner, rep_name

Specificites par plateforme

Chaque outil envoie les donnees dans un format different. Voici ou placer les champs importants selon votre outil.

GHL (GoHighLevel)

Les UTMs et le produit vont dans les custom fields du contact :

json
{
  "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 donnees brutes.

Voir le payload GHL complet (lead)
json
{
  "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"
  }
}
Voir le payload GHL complet (booking)
json
{
  "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"
}
Voir le payload GHL complet (vente)
json
{
  "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_ai

Les reponses du formulaire (email, telephone, nom, questions custom) sont extraites automatiquement depuis le tableau answers.

Voir le payload Typeform complet
json
{
  "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 reservation contient les parametres UTM. Les questions personnalisees sont dans questions_and_answers.

L'URL doit inclure ?client_key=VOTRE_CLE.

Voir le payload Calendly complet
json
{
  "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 detecte par sa signature.

Voir le payload Stripe complet
json
{
  "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 etre dans l'URL.

Voir le payload Zoho CRM complet
json
{
  "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 etre dans l'URL (Zoho Books ne l'envoie pas dans le payload). Les custom fields Zoho sont prefixes par cf_.

Voir le payload Zoho Books complet
json
{
  "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.

Voir le payload Pipedrive complet (deal gagne)
json
{
  "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 :

bash
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 verifiez dans DURUM.ai > Logs Data que l'evenement apparait.


Depannage rapide

SymptomeCauseSolution
Evenement absent de l'appclient_key manquant ou invalideAjoutez ?client_key=VOTRE_CLE a l'URL
Statut quarantinedFormulaire ou client non reconnuNormal pour un nouveau formulaire. L'agence le categorise.
Reponse deduped: trueEvenement deja recuNormal. Le doublon a ete detecte.
UTMs vides dans le dashboardPas d'UTMs dans le webhookConfigurez les hidden fields (Typeform), custom fields (GHL) ou tracking (Calendly)
Montant a $0 sur une venteChamp amount absentAjoutez le montant dans le payload
Reponse 401Authentification echoueePour GHL : ajoutez &source=ghl. Pour Stripe : verifiez la signature.
Reponse 429Trop de requetesMaximum 60 par minute par IP
Reponse 202Serveur temporairement occupeL'evenement est en file d'attente et sera traite sous quelques minutes

Propulse par Durum Marketing